diff --git a/Sources/ProjectSpec/SpecFile.swift b/Sources/ProjectSpec/SpecFile.swift index 18ea15628..582165508 100644 --- a/Sources/ProjectSpec/SpecFile.swift +++ b/Sources/ProjectSpec/SpecFile.swift @@ -1,6 +1,7 @@ import Foundation import JSONUtilities import PathKit +import XcodeGenCore import Yams public struct SpecFile { @@ -26,24 +27,62 @@ public struct SpecFile { static let defaultEnable = true init?(any: Any) { - if let string = any as? String { - path = Path(string) - relativePaths = Include.defaultRelativePaths - enable = Include.defaultEnable + if let path = any as? String { + self.init( + path: Path(path), + relativePaths: Include.defaultRelativePaths, + enable: Include.defaultEnable + ) } else if let dictionary = any as? JSONDictionary, let path = dictionary["path"] as? String { - self.path = Path(path) - relativePaths = Self.resolveBoolean(dictionary, key: "relativePaths") ?? Include.defaultRelativePaths - enable = Self.resolveBoolean(dictionary, key: "enable") ?? Include.defaultEnable + self.init( + path: Path(path), + dictionary: dictionary + ) } else { return nil } } + + private init(path: Path, relativePaths: Bool, enable: Bool) { + self.path = path + self.relativePaths = relativePaths + self.enable = enable + } + + private init?(path: Path, dictionary: JSONDictionary) { + self.path = path + relativePaths = Self.resolveBoolean(dictionary, key: "relativePaths") ?? Include.defaultRelativePaths + enable = Self.resolveBoolean(dictionary, key: "enable") ?? Include.defaultEnable + } + + private func replacingPath(with newPath: Path) -> Include { + Include( + path: newPath, + relativePaths: relativePaths, + enable: enable + ) + } + + private static func includes(from array: [Any], basePath: Path) -> [Include] { + array.flatMap { entry -> [Include] in + if let string = entry as? String, let include = Include(any: string) { + return [include] + } else if let dictionary = entry as? JSONDictionary, let path = dictionary["path"] as? String { + return Glob(pattern: (basePath + Path(path)).normalize().string) + .compactMap { Include(path: Path($0), dictionary: dictionary) } + } else { + return [] + } + } + } - static func parse(json: Any?) -> [Include] { + static func parse(json: Any?, basePath: Path) -> [Include] { if let array = json as? [Any] { - return array.compactMap(Include.init) + return includes(from: array, basePath: basePath) } else if let object = json, let include = Include(any: object) { - return [include] + return Glob(pattern: (basePath + include.path.normalize()).string) + .compactMap { try? Path($0).relativePath(from: basePath) } + .compactMap { include.replacingPath(with: $0) } } else { return [] } @@ -92,7 +131,7 @@ public struct SpecFile { let jsonDictionary = try SpecFile.loadDictionary(path: path).expand(variables: variables) - let includes = Include.parse(json: jsonDictionary["include"]) + let includes = Include.parse(json: jsonDictionary["include"], basePath: basePath) let subSpecs: [SpecFile] = try includes .filter(\.enable) .map { include in diff --git a/Tests/Fixtures/glob_include/glob_include_sut.yml b/Tests/Fixtures/glob_include/glob_include_sut.yml new file mode 100644 index 000000000..ce228f2f0 --- /dev/null +++ b/Tests/Fixtures/glob_include/glob_include_sut.yml @@ -0,0 +1,3 @@ +include: + - path: "*/*.yml" +name: GlobImportDependent \ No newline at end of file diff --git a/Tests/Fixtures/glob_include/glob_include_sut_string.yml b/Tests/Fixtures/glob_include/glob_include_sut_string.yml new file mode 100644 index 000000000..f5dcffda4 --- /dev/null +++ b/Tests/Fixtures/glob_include/glob_include_sut_string.yml @@ -0,0 +1,2 @@ +include: "*/*.yml" +name: GlobImportDependent \ No newline at end of file diff --git a/Tests/Fixtures/glob_include/includes/a_yaml.yml b/Tests/Fixtures/glob_include/includes/a_yaml.yml new file mode 100644 index 000000000..7bc4f2324 --- /dev/null +++ b/Tests/Fixtures/glob_include/includes/a_yaml.yml @@ -0,0 +1,4 @@ +name: DuplicatedImportRoot +fileGroups: + - First + - Second \ No newline at end of file diff --git a/Tests/Fixtures/glob_include/includes/b_yaml.yml b/Tests/Fixtures/glob_include/includes/b_yaml.yml new file mode 100644 index 000000000..6951d122e --- /dev/null +++ b/Tests/Fixtures/glob_include/includes/b_yaml.yml @@ -0,0 +1,2 @@ +fileGroups: + - Third \ No newline at end of file diff --git a/Tests/ProjectSpecTests/SpecLoadingTests.swift b/Tests/ProjectSpecTests/SpecLoadingTests.swift index 6a81c0907..cb1e3bedb 100644 --- a/Tests/ProjectSpecTests/SpecLoadingTests.swift +++ b/Tests/ProjectSpecTests/SpecLoadingTests.swift @@ -25,6 +25,28 @@ class SpecLoadingTests: XCTestCase { } } + func testSpecLoaderGlobImports() { + describe { + $0.it("includes files via glob") { + let path = fixturePath + "glob_include/glob_include_sut.yml" + let project = try loadSpec(path: path) + + try expect(project.fileGroups) == ["First", "Second", "Third"] + } + } + } + + func testSpecLoaderGlobImportsString() { + describe { + $0.it("includes files via glob") { + let path = fixturePath + "glob_include/glob_include_sut_string.yml" + let project = try loadSpec(path: path) + + try expect(project.fileGroups) == ["First", "Second", "Third"] + } + } + } + func testSpecLoader() { describe { $0.it("merges includes") {