File: ActoolInputFileGroupingStrategy.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (68 lines) | stat: -rw-r--r-- 3,462 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

public import SWBCore
import SWBUtil
import Foundation

/// A grouping strategy that groups all asset catalogs and all strings files that match sticker packs inside those asset catalogs.
@_spi(Testing) public final class ActoolInputFileGroupingStrategy: InputFileGroupingStrategy {

    /// Group identifier that’s returned for every path.
    let groupIdentifier: String

    @_spi(Testing) public init(groupIdentifier: String) {
        self.groupIdentifier = groupIdentifier
    }

    /// Always just returns the identifier with which the grouping strategy was initialized.
    public func determineGroupIdentifier(groupable: any InputFileGroupable) -> String? {
        return groupIdentifier
    }

    public func groupAdditionalFiles<S: Sequence>(to target: FileToBuildGroup, from source: S, context: any InputFileGroupingStrategyContext) -> [FileToBuildGroup] where S.Element == FileToBuildGroup {
        // TODO Should we make this a property of the product type?
        guard context.productType?.identifier == "com.apple.product-type.app-extension.messages-sticker-pack" else { return [] }

        let fileTypes = ["folder.assetcatalog", "folder.stickers", "folder.iconcomposer.icon"]
        let catalogFileTypes = fileTypes.map { context.lookupFileType(identifier: $0)! }
        let stringsFileType = context.lookupFileType(identifier: "text.plist.strings")!

        let catalogPaths = target.files.compactMap { ftb in catalogFileTypes.contains { ftb.fileType.conformsTo($0) } ? ftb.absolutePath : nil }

        // Asserting because if this grouper is used on a rule that isn't processing asset catalogs, we have a bug
        assert(!catalogPaths.isEmpty, "Expected asset catalogs in \(target.files)")

        do {
            guard let stickerPackName = try ActoolInputFileGroupingStrategy.stickerPackName(inCatalogs: catalogPaths, fs: context.fs) else { return [] }
            return source.filter { group in
                group.files.contains { file in
                    file.fileType.conformsTo(stringsFileType) && file.absolutePath.basenameWithoutSuffix == stickerPackName
                }
            }
        }
        catch {
            context.error("\(error)", location: .unknown, component: .default)
            return []
        }
    }

    /// See IBICStickerPackScanner. Note: Only one sticker pack is supported per extension; actool will warn and pick one if there are multiple, so we're just going to return the first we see.
    static func stickerPackName(inCatalogs catalogs: [Path], fs: any FSProxy) throws -> String? {
        return try catalogs.flatMap { catalog in
            return try fs.listdir(catalog).compactMap { item in
                let path = Path(item)
                return path.fileExtension.caseInsensitiveCompare("stickerpack") == .orderedSame ? path.basenameWithoutSuffix : nil
            }
        }.min() /* Use .min() instead of .first to make it deterministic. */
    }
}