From 366dd0ba3f0876b88018cb5ce1e8b4fe0fe8fee9 Mon Sep 17 00:00:00 2001 From: Adam Khazi Date: Tue, 30 Jul 2019 15:18:05 +0100 Subject: [PATCH] new api --- Sources/ProjectDescription/TuistConfig.swift | 118 +++++++++++---- .../TuistGenerator/Models/TuistConfig.swift | 134 +++++++++++++++++- .../Generator/GeneratorModelLoader.swift | 25 ++-- .../Generator/GeneratorModelLoaderTests.swift | 5 +- .../TuistConfig.swift | 5 +- 5 files changed, 241 insertions(+), 46 deletions(-) diff --git a/Sources/ProjectDescription/TuistConfig.swift b/Sources/ProjectDescription/TuistConfig.swift index 1da777cbcd2..f0e1bed9200 100644 --- a/Sources/ProjectDescription/TuistConfig.swift +++ b/Sources/ProjectDescription/TuistConfig.swift @@ -7,8 +7,7 @@ public class TuistConfig: Encodable, Decodable { /// - generateManifestElement: When passed, Tuist generates the projects, targets and schemes to compile the project manifest. public enum GenerationOption: Encodable, Decodable, Equatable { case generateManifest - case suffixProjectNames(with: String) - case prefixProjectNames(with: String) + case xcodeProjectName(TemplateString) } /// Generation options. @@ -23,12 +22,82 @@ public class TuistConfig: Encodable, Decodable { } } +public struct TemplateString: Encodable, Decodable { + let rawString: String +} + +extension TemplateString: ExpressibleByStringLiteral { + public init(stringLiteral: String) { + rawString = stringLiteral + } +} + +extension TemplateString: CustomStringConvertible { + public var description: String { + return rawString + } +} + +extension TemplateString: ExpressibleByStringInterpolation { + public init(stringInterpolation: StringInterpolation) { + rawString = stringInterpolation.string + } + + public struct StringInterpolation: StringInterpolationProtocol { + var string: String + + public init(literalCapacity _: Int, interpolationCount _: Int) { + string = String() + } + + public mutating func appendLiteral(_ literal: String) { + string.append(literal) + } + + public func appendInterpolation(_: String) {} + } +} + +extension TemplateString { + public enum Token: String { + case projectName = "${project_name}" + } +} + +public extension TemplateString.StringInterpolation { + mutating func appendInterpolation(_ token: TemplateString.Token) { + string.append(token.rawValue) + } +} + +extension TemplateString.Token { + enum CodingKeys: String, CodingKey { + case projectName + } + + internal init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + + let enumCase = try container.decode(String.self) + switch enumCase { + case CodingKeys.projectName.rawValue: self = .projectName + default: throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "Unknown enum case '\(enumCase)'")) + } + } + + internal func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + + switch self { + case .projectName: try container.encode(CodingKeys.projectName.rawValue) + } + } +} + extension TuistConfig.GenerationOption { enum CodingKeys: String, CodingKey { case generateManifest - case suffixProjectNames - case prefixProjectNames - case with + case xcodeProjectName } public init(from decoder: Decoder) throws { @@ -38,16 +107,10 @@ extension TuistConfig.GenerationOption { self = .generateManifest return } - if container.allKeys.contains(.suffixProjectNames), try container.decodeNil(forKey: .suffixProjectNames) == false { - let associatedValues = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .suffixProjectNames) - let with = try associatedValues.decode(String.self, forKey: .with) - self = .suffixProjectNames(with: with) - return - } - if container.allKeys.contains(.prefixProjectNames), try container.decodeNil(forKey: .prefixProjectNames) == false { - let associatedValues = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .prefixProjectNames) - let with = try associatedValues.decode(String.self, forKey: .with) - self = .prefixProjectNames(with: with) + if container.allKeys.contains(.xcodeProjectName), try container.decodeNil(forKey: .xcodeProjectName) == false { + var associatedValues = try container.nestedUnkeyedContainer(forKey: .xcodeProjectName) + let associatedValue0 = try associatedValues.decode(TemplateString.self) + self = .xcodeProjectName(associatedValue0) return } throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "Unknown enum case")) @@ -59,24 +122,29 @@ extension TuistConfig.GenerationOption { switch self { case .generateManifest: _ = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .generateManifest) - case let .suffixProjectNames(with): - var associatedValues = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .suffixProjectNames) - try associatedValues.encode(with, forKey: .with) - case let .prefixProjectNames(with): - var associatedValues = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .prefixProjectNames) - try associatedValues.encode(with, forKey: .with) + case let .xcodeProjectName(associatedValue0): + var associatedValues = container.nestedUnkeyedContainer(forKey: .xcodeProjectName) + try associatedValues.encode(associatedValue0) } } } +extension TemplateString.Token: Equatable {} +public func == (lhs: TemplateString.Token, rhs: TemplateString.Token) -> Bool { + switch (lhs, rhs) { + case (.projectName, .projectName): + return true + } +} + +// MARK: - TuistConfig.GenerationOption AutoEquatable + public func == (lhs: TuistConfig.GenerationOption, rhs: TuistConfig.GenerationOption) -> Bool { switch (lhs, rhs) { case (.generateManifest, .generateManifest): return true - case let (.suffixProjectNames(lhs), .suffixProjectNames(rhs)): - return lhs == rhs - case let (.prefixProjectNames(lhs), .prefixProjectNames(rhs)): - return lhs == rhs + case let (.xcodeProjectName(lhs), .xcodeProjectName(rhs)): + return lhs.rawString == rhs.rawString default: return false } } diff --git a/Sources/TuistGenerator/Models/TuistConfig.swift b/Sources/TuistGenerator/Models/TuistConfig.swift index f5463f13ed2..0a262cd30f0 100644 --- a/Sources/TuistGenerator/Models/TuistConfig.swift +++ b/Sources/TuistGenerator/Models/TuistConfig.swift @@ -9,8 +9,7 @@ public class TuistConfig: Equatable, Hashable { /// - generateManifestElement: When passed, Tuist generates the projects, targets and schemes to compile the project manifest. public enum GenerationOption: Hashable { case generateManifest - case suffixProjectNames(with: String) - case prefixProjectNames(with: String) + case xcodeProjectName(TemplateString) } /// Generation options. @@ -51,3 +50,134 @@ public class TuistConfig: Equatable, Hashable { return lhs.generationOptions == rhs.generationOptions } } + +public struct TemplateString: Encodable, Decodable, Hashable { + public let rawString: String + public init(rawString: String) { + self.rawString = rawString + } +} + +extension TemplateString: ExpressibleByStringLiteral { + public init(stringLiteral: String) { + rawString = stringLiteral + } +} + +extension TemplateString: CustomStringConvertible { + public var description: String { + return rawString + } +} + +extension TemplateString: ExpressibleByStringInterpolation { + public init(stringInterpolation: StringInterpolation) { + rawString = stringInterpolation.string + } + + public struct StringInterpolation: StringInterpolationProtocol { + var string: String + + public init(literalCapacity _: Int, interpolationCount _: Int) { + string = String() + } + + public mutating func appendLiteral(_ literal: String) { + string.append(literal) + } + + public func appendInterpolation(_: String) {} + } +} + +extension TemplateString { + public enum Token: String { + case projectName = "${project_name}" + } +} + +extension TemplateString.StringInterpolation { + public mutating func appendInterpolation(_ token: TemplateString.Token) { + string.append(token.rawValue) + } +} + +extension TemplateString.Token { + enum CodingKeys: String, CodingKey { + case projectName + } + + internal init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + + let enumCase = try container.decode(String.self) + switch enumCase { + case CodingKeys.projectName.rawValue: self = .projectName + default: throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "Unknown enum case '\(enumCase)'")) + } + } + + internal func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + + switch self { + case .projectName: try container.encode(CodingKeys.projectName.rawValue) + } + } +} + +extension TuistConfig.GenerationOption { + enum CodingKeys: String, CodingKey { + case generateManifest + case xcodeProjectName + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + if container.allKeys.contains(.generateManifest), try container.decodeNil(forKey: .generateManifest) == false { + self = .generateManifest + return + } + if container.allKeys.contains(.xcodeProjectName), try container.decodeNil(forKey: .xcodeProjectName) == false { + var associatedValues = try container.nestedUnkeyedContainer(forKey: .xcodeProjectName) + let associatedValue0 = try associatedValues.decode(TemplateString.self) + self = .xcodeProjectName(associatedValue0) + return + } + throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "Unknown enum case")) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + switch self { + case .generateManifest: + _ = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .generateManifest) + case let .xcodeProjectName(associatedValue0): + var associatedValues = container.nestedUnkeyedContainer(forKey: .xcodeProjectName) + try associatedValues.encode(associatedValue0) + } + } +} + +extension TemplateString.Token: Equatable {} +public func == (lhs: TemplateString.Token, rhs: TemplateString.Token) -> Bool { + switch (lhs, rhs) { + case (.projectName, .projectName): + return true + } +} + +// MARK: - TuistConfig.GenerationOption AutoEquatable + +extension TuistConfig.GenerationOption: Equatable {} +public func == (lhs: TuistConfig.GenerationOption, rhs: TuistConfig.GenerationOption) -> Bool { + switch (lhs, rhs) { + case (.generateManifest, .generateManifest): + return true + case let (.xcodeProjectName(lhs), .xcodeProjectName(rhs)): + return lhs == rhs + default: return false + } +} diff --git a/Sources/TuistKit/Generator/GeneratorModelLoader.swift b/Sources/TuistKit/Generator/GeneratorModelLoader.swift index dec4edb0570..6def176f54d 100644 --- a/Sources/TuistKit/Generator/GeneratorModelLoader.swift +++ b/Sources/TuistKit/Generator/GeneratorModelLoader.swift @@ -125,10 +125,8 @@ extension TuistGenerator.TuistConfig.GenerationOption { switch manifest { case .generateManifest: return .generateManifest - case let .suffixProjectNames(suffixRaw): - return .suffixProjectNames(with: suffixRaw) - case let .prefixProjectNames(prefixRaw): - return .prefixProjectNames(with: prefixRaw) + case let .xcodeProjectName(templateString): + return .xcodeProjectName(TemplateString(rawString: templateString.description)) } } } @@ -237,19 +235,20 @@ extension TuistGenerator.Project { printer: printer) } - let xcodeProjFileName = tuistConfig.generationOptions.reduce(name) { acc, item in - if case let .prefixProjectNames(prefixRaw) = item { - return prefixRaw + acc + var xcodeFilename = tuistConfig.generationOptions.compactMap { (item) -> String? in + if case let .xcodeProjectName(projectName) = item { + return projectName.rawString } - if case let .suffixProjectNames(suffixRaw) = item { - return acc + suffixRaw - } - return acc - } + return nil + }.first + + let projectNameTemplate = TuistGenerator.TemplateString.Token.projectName.rawValue + xcodeFilename = xcodeFilename? + .replacingOccurrences(of: projectNameTemplate, with: manifest.name) return Project(path: path, name: name, - fileName: xcodeProjFileName, + fileName: xcodeFilename, settings: settings ?? .default, filesGroup: .group(name: "Project"), targets: targets, diff --git a/Tests/TuistKitTests/Generator/GeneratorModelLoaderTests.swift b/Tests/TuistKitTests/Generator/GeneratorModelLoaderTests.swift index 8c4bc419f41..71a8e91ea85 100644 --- a/Tests/TuistKitTests/Generator/GeneratorModelLoaderTests.swift +++ b/Tests/TuistKitTests/Generator/GeneratorModelLoaderTests.swift @@ -186,8 +186,7 @@ class GeneratorModelLoaderTest: XCTestCase { ]), ] let configs = [ - path: ProjectDescription.TuistConfig.test(generationOptions: [.prefixProjectNames(with: "something-"), - .suffixProjectNames(with: "-something")]), + path: ProjectDescription.TuistConfig.test(generationOptions: [.xcodeProjectName("one \(.projectName) two")]), ] let manifestLoader = createManifestLoader(with: manifests, configs: configs) let subject = GeneratorModelLoader(fileHandler: fileHandler, @@ -198,7 +197,7 @@ class GeneratorModelLoaderTest: XCTestCase { let model = try subject.loadProject(at: path) // Then - XCTAssertEqual(model.fileName, "something-SomeProject-something") + XCTAssertEqual(model.fileName, "one SomeProject two") } func test_loadWorkspace() throws { diff --git a/fixtures/app_with_framework_and_tests_with_custom_filenames/TuistConfig.swift b/fixtures/app_with_framework_and_tests_with_custom_filenames/TuistConfig.swift index fb6a9823d1f..c21d66b9232 100644 --- a/fixtures/app_with_framework_and_tests_with_custom_filenames/TuistConfig.swift +++ b/fixtures/app_with_framework_and_tests_with_custom_filenames/TuistConfig.swift @@ -3,7 +3,6 @@ import ProjectDescription let config = TuistConfig( generationOptions: [ .generateManifest, - .prefixProjectNames(with: "AwesomePrefix-"), - .suffixProjectNames(with: "-AwesomeSuffix") + .xcodeProjectName("AwesomePrefix-\(.projectName)-AwesomeSuffix") ] -) \ No newline at end of file +)