Files
David Jennes 4d1d3591c7 Bump year
2022-07-30 20:47:39 +02:00

121 lines
4.3 KiB
Swift

//
// SwiftGen
// Copyright © 2022 SwiftGen
// MIT Licence
//
import PathKit
// MARK: - Config.Entry
// Note: there's a bug in SPM which causes compilation to fail because of the compilation order.
// Once it is fixed, we should move `ConfigEntry` back into an extension of `Config`.
// https://bugs.swift.org/browse/SR-5734
public struct ConfigEntry {
enum Keys {
static let inputs = "inputs"
static let filter = "filter"
static let options = "options"
static let outputs = "outputs"
// Legacy: remove this once we stop supporting the output key at subcommand level
static let output = "output"
static let params = "params"
static let templateName = "templateName"
static let templatePath = "templatePath"
// Legacy: remove this once we stop supporting the old paths key (replaced by inputs)
static let paths = "paths"
}
var inputs: [Path]
var filter: String?
var options: [String: Any]
var outputs: [ConfigEntryOutput]
mutating func makeRelativeTo(inputDir: Path?, outputDir: Path?) {
if let inputDir = inputDir {
self.inputs = self.inputs.map { $0.isRelative ? inputDir + $0 : $0 }
}
self.outputs = self.outputs.map { output in
var result = output
result.makingRelativeTo(outputDir: outputDir)
return result
}
}
}
extension ConfigEntry {
init(yaml: [String: Any], cmd: String, logger: (LogLevel, String) -> Void) throws {
if let inputs = yaml[Keys.inputs] {
self.inputs = try Self.parseValueOrArray(yaml: inputs, key: Keys.inputs, parseValue: Path.init(stringLiteral:))
} else if let inputs = yaml[Keys.paths] {
// Legacy: remove this once we stop supporting the old paths key (replaced by inputs)
logger(.warning, "\(cmd): `paths` is a deprecated in favour of `inputs`.")
self.inputs = try Self.parseValueOrArray(yaml: inputs, key: Keys.inputs, parseValue: Path.init(stringLiteral:))
} else {
throw Config.Error.missingEntry(key: Keys.inputs)
}
filter = yaml[Keys.filter] as? String
options = try Self.getOptionalField(yaml: yaml, key: Keys.options) ?? [:]
if let outputs = yaml[Keys.outputs] {
do {
self.outputs = try ConfigEntryOutput.parseCommandOutput(yaml: outputs, logger: logger)
} catch let error as Config.Error {
throw error.withKeyPrefixed(by: Keys.outputs)
}
} else if yaml[Keys.output] != nil {
// Legacy: remove this once we stop supporting the output key at subcommand level
// The config still contains the old style where all properties command properties
// are at the same level
self.outputs = try ConfigEntryOutput.parseCommandOutput(yaml: yaml, logger: logger)
} else {
throw Config.Error.missingEntry(key: Keys.outputs)
}
// Warn about deprecated parameters
// Legacy: remove this once we stop supporting the output key at subcommand level
for key in [Keys.output, Keys.params, Keys.templateName, Keys.templatePath] where yaml[key] != nil {
logger(.warning, "\(cmd): `\(key)` is a deprecated in favour of `outputs.\(key)`.")
}
}
static func parseCommandEntry(yaml: Any, cmd: String, logger: (LogLevel, String) -> Void) throws -> [ConfigEntry] {
try parseValueOrArray(yaml: yaml) { yaml in
try ConfigEntry(yaml: yaml, cmd: cmd, logger: logger)
}
}
static func getOptionalField<T>(yaml: [String: Any], key: String) throws -> T? {
guard let value = yaml[key] else {
return nil
}
guard let typedValue = value as? T else {
throw Config.Error.wrongType(key: key, expected: String(describing: T.self), got: type(of: value))
}
return typedValue
}
static func parseValueOrArray<T, U>(yaml: Any, key: String? = nil, parseValue: (T) throws -> U) throws -> [U] {
if let value = yaml as? T {
return [try parseValue(value)]
} else if let value = yaml as? [T] {
return try value.map { try parseValue($0) }
} else {
throw Config.Error.wrongType(key: key, expected: "\(T.self) or Array of \(T.self)", got: type(of: yaml))
}
}
}
/// Convert to CommandLine-equivalent string (for verbose mode, printing linting info, )
///
extension ConfigEntry {
func commandLine(forCommand cmd: String) -> [String] {
outputs.map { output in
output.commandLine(forCommand: cmd, inputs: inputs, filter: filter, options: options)
}
}
}