import PureYAML public extension PureYAML.JSON { /// Standalone YAML <-> JSON conversion. It depends only on the parsing/emitting /// engine and the JSON writer; it knows nothing about any editor or UI, so it is /// usable on its own (a CLI, a server, a build step, a test). /// /// A conversion succeeds only when, in order: /// 1. the source parses, /// 2. the value is representable in the target format, and /// 3. the emitted output parses back to a value identical to the source /// (a lossless round-trip check that also guards against emitter defects). /// /// JSON is a subset of YAML 1.2, so the same engine parses both the source and /// the re-parsed output; only "to JSON" can be rejected for representability (a /// value may use a non-string key or a non-finite number). "To YAML" still /// rejects duplicate keys, which are legal in JSON but not in YAML. enum Convert { /// Convert source text to the target format, validated end to end. public static func convert(_ source: String, to target: Format, pretty: Bool = true) throws -> String { let value = try parse(source) let output = try emit(value, as: target, pretty: pretty) guard try parse(output) == value else { throw Failure.roundTripMismatch(target) } return output } /// Emit a value tree in the target format. The value is validated for /// representability first; the round-trip check is not run here (use /// ``convert(_:to:pretty:)`` for the fully validated path). public static func emit(_ value: PureYAML.Model.Value, as target: Format, pretty: Bool = true) throws -> String { let issues = validator(for: target).collect(value).values guard issues.isEmpty else { throw Failure.notRepresentable(issues) } switch target { case .json: return try PureYAML.JSON.Writer.string(from: value, options: .init(pretty: pretty, indent: 2)) case .yaml: return PureYAML.dump(value, options: .init(scalarStyle: .plainWhenSafe, collectionStyle: .block)) } } /// The validator for a target format. JSON additionally requires string keys /// and finite numbers; YAML allows complex keys but still forbids duplicates. public static func validator(for target: Format) -> PureYAML.Validation.Validator { switch target { case .json: PureYAML.JSON.Validator.representability case .yaml: PureYAML.Validation.Validator() } } private static func parse(_ text: String) throws -> PureYAML.Model.Value { do { return try PureYAML.parse(text) } catch { throw Failure.sourceInvalid(String(describing: error)) } } } }