import Foundation import Yams // MARK: - YamlParser /// An interface for parsing YAML. struct YamlParser { /// Parses the input YAML string as an untyped dictionary. /// /// - parameter yaml: YAML-formatted string. /// - parameter env: The environment to use to expand variables in the YAML. /// /// - returns: The parsed YAML as an untyped dictionary. /// /// - throws: Throws if the `yaml` string provided could not be parsed. static func parse(_ yaml: String, env: [String: String] = ProcessInfo.processInfo.environment) throws -> [String: Any] { do { return try Yams.load(yaml: yaml, .default, .swiftlintConstructor(env: env)) as? [String: Any] ?? [:] } catch { throw Issue.yamlParsing("\(error)") } } } private extension Constructor { static func swiftlintConstructor(env: [String: String]) -> Constructor { Constructor(customScalarMap(env: env)) } static func customScalarMap(env: [String: String]) -> ScalarMap { var map = defaultScalarMap map[.str] = { $0.string.expandingEnvVars(env: env) } map[.bool] = { switch $0.string.expandingEnvVars(env: env).lowercased() { case "true": true case "false": false default: nil } } map[.int] = { Int($0.string.expandingEnvVars(env: env)) } map[.float] = { Double($0.string.expandingEnvVars(env: env)) } return map } } private extension String { func expandingEnvVars(env: [String: String]) -> String { guard contains("${") else { // No environment variables used. return self } return env.reduce(into: self) { result, envVar in result = result.replacingOccurrences(of: "${\(envVar.key)}", with: envVar.value) } } }