From 633547d126c0d219fd5ed2094cac18c01ad47f3a Mon Sep 17 00:00:00 2001 From: Tejas Sharma Date: Wed, 7 Feb 2024 11:17:18 -0800 Subject: [PATCH 1/6] add option for newline encoding --- Sources/Yams/Emitter.swift | 36 +++++++++++++++++++++++++----------- Sources/Yams/Encoder.swift | 16 ++++++++++++---- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/Sources/Yams/Emitter.swift b/Sources/Yams/Emitter.swift index f2d0ffe6..6eeee9aa 100644 --- a/Sources/Yams/Emitter.swift +++ b/Sources/Yams/Emitter.swift @@ -41,7 +41,8 @@ public func dump( version: (major: Int, minor: Int)? = nil, sortKeys: Bool = false, sequenceStyle: Node.Sequence.Style = .any, - mappingStyle: Node.Mapping.Style = .any) throws -> String + mappingStyle: Node.Mapping.Style = .any, + newLineScalarStyle: Node.Scalar.Style = .any) throws -> String where Objects: Sequence { func representable(from object: Any) throws -> NodeRepresentable { if let representable = object as? NodeRepresentable { @@ -62,7 +63,8 @@ public func dump( version: version, sortKeys: sortKeys, sequenceStyle: sequenceStyle, - mappingStyle: mappingStyle + mappingStyle: mappingStyle, + newLineScalarStyle: newLineScalarStyle ) } @@ -96,7 +98,8 @@ public func dump( version: (major: Int, minor: Int)? = nil, sortKeys: Bool = false, sequenceStyle: Node.Sequence.Style = .any, - mappingStyle: Node.Mapping.Style = .any) throws -> String { + mappingStyle: Node.Mapping.Style = .any, + newLineScalarStyle: Node.Scalar.Style = .any) throws -> String { return try serialize( node: object.represented(), canonical: canonical, @@ -109,7 +112,8 @@ public func dump( version: version, sortKeys: sortKeys, sequenceStyle: sequenceStyle, - mappingStyle: mappingStyle + mappingStyle: mappingStyle, + newLineScalarStyle: newLineScalarStyle ) } @@ -143,7 +147,8 @@ public func serialize( version: (major: Int, minor: Int)? = nil, sortKeys: Bool = false, sequenceStyle: Node.Sequence.Style = .any, - mappingStyle: Node.Mapping.Style = .any) throws -> String + mappingStyle: Node.Mapping.Style = .any, + newLineScalarStyle: Node.Scalar.Style = .any) throws -> String where Nodes: Sequence, Nodes.Iterator.Element == Node { let emitter = Emitter( canonical: canonical, @@ -156,7 +161,8 @@ public func serialize( version: version, sortKeys: sortKeys, sequenceStyle: sequenceStyle, - mappingStyle: mappingStyle + mappingStyle: mappingStyle, + newLineScalarStyle: newLineScalarStyle ) try emitter.open() try nodes.forEach(emitter.serialize) @@ -194,7 +200,8 @@ public func serialize( version: (major: Int, minor: Int)? = nil, sortKeys: Bool = false, sequenceStyle: Node.Sequence.Style = .any, - mappingStyle: Node.Mapping.Style = .any) throws -> String { + mappingStyle: Node.Mapping.Style = .any, + newLineScalarStyle: Node.Scalar.Style = .any) throws -> String { return try serialize( nodes: [node], canonical: canonical, @@ -207,7 +214,8 @@ public func serialize( version: version, sortKeys: sortKeys, sequenceStyle: sequenceStyle, - mappingStyle: mappingStyle + mappingStyle: mappingStyle, + newLineScalarStyle: newLineScalarStyle ) } @@ -254,6 +262,9 @@ public final class Emitter { /// Set the style for mappings (dictionaries) public var mappingStyle: Node.Mapping.Style = .any + + /// Set the style for scalars that include newlines + public var newLineScalarStyle: Node.Scalar.Style = .any } /// Configuration options to use when emitting YAML. @@ -287,7 +298,8 @@ public final class Emitter { version: (major: Int, minor: Int)? = nil, sortKeys: Bool = false, sequenceStyle: Node.Sequence.Style = .any, - mappingStyle: Node.Mapping.Style = .any) { + mappingStyle: Node.Mapping.Style = .any, + newLineScalarStyle: Node.Scalar.Style = .any) { options = Options(canonical: canonical, indent: indent, width: width, @@ -298,7 +310,8 @@ public final class Emitter { version: version, sortKeys: sortKeys, sequenceStyle: sequenceStyle, - mappingStyle: mappingStyle) + mappingStyle: mappingStyle, + newLineScalarStyle: newLineScalarStyle) // configure emitter yaml_emitter_initialize(&emitter) yaml_emitter_set_output(&self.emitter, { pointer, buffer, size in @@ -420,7 +433,7 @@ extension Emitter.Options { public init(canonical: Bool = false, indent: Int = 0, width: Int = 0, allowUnicode: Bool = false, lineBreak: Emitter.LineBreak = .ln, version: (major: Int, minor: Int)? = nil, sortKeys: Bool = false, sequenceStyle: Node.Sequence.Style = .any, - mappingStyle: Node.Mapping.Style = .any) { + mappingStyle: Node.Mapping.Style = .any, newLineScalarStyle: Node.Scalar.Style = .any) { self.canonical = canonical self.indent = indent self.width = width @@ -430,6 +443,7 @@ extension Emitter.Options { self.sortKeys = sortKeys self.sequenceStyle = sequenceStyle self.mappingStyle = mappingStyle + self.newLineScalarStyle = newLineScalarStyle } } diff --git a/Sources/Yams/Encoder.swift b/Sources/Yams/Encoder.swift index 9c6d8549..5a68c913 100644 --- a/Sources/Yams/Encoder.swift +++ b/Sources/Yams/Encoder.swift @@ -29,7 +29,7 @@ public class YAMLEncoder { public func encode(_ value: T, userInfo: [CodingUserInfoKey: Any] = [:]) throws -> String { do { let encoder = _Encoder(userInfo: userInfo, sequenceStyle: options.sequenceStyle, - mappingStyle: options.mappingStyle) + mappingStyle: options.mappingStyle, newlineScalarStyle: options.newLineScalarStyle) var container = encoder.singleValueContainer() try container.encode(value) return try serialize(node: encoder.node, options: options) @@ -49,11 +49,12 @@ private class _Encoder: Swift.Encoder { var node: Node = .unused init(userInfo: [CodingUserInfoKey: Any] = [:], codingPath: [CodingKey] = [], sequenceStyle: Node.Sequence.Style, - mappingStyle: Node.Mapping.Style) { + mappingStyle: Node.Mapping.Style, newlineScalarStyle: Node.Scalar.Style) { self.userInfo = userInfo self.codingPath = codingPath self.sequenceStyle = sequenceStyle self.mappingStyle = mappingStyle + self.newlineScalarStyle = newlineScalarStyle } // MARK: - Swift.Encoder Methods @@ -62,6 +63,7 @@ private class _Encoder: Swift.Encoder { let userInfo: [CodingUserInfoKey: Any] let sequenceStyle: Node.Sequence.Style let mappingStyle: Node.Mapping.Style + let newlineScalarStyle: Node.Scalar.Style func container(keyedBy type: Key.Type) -> KeyedEncodingContainer { if canEncodeNewValue { @@ -124,14 +126,14 @@ private class _ReferencingEncoder: _Encoder { self.encoder = encoder reference = .mapping(key.stringValue) super.init(userInfo: encoder.userInfo, codingPath: encoder.codingPath + [key], - sequenceStyle: encoder.sequenceStyle, mappingStyle: encoder.mappingStyle) + sequenceStyle: encoder.sequenceStyle, mappingStyle: encoder.mappingStyle, newlineScalarStyle: encoder.newlineScalarStyle) } init(referencing encoder: _Encoder, at index: Int) { self.encoder = encoder reference = .sequence(index) super.init(userInfo: encoder.userInfo, codingPath: encoder.codingPath + [_YAMLCodingKey(index: index)], - sequenceStyle: encoder.sequenceStyle, mappingStyle: encoder.mappingStyle) + sequenceStyle: encoder.sequenceStyle, mappingStyle: encoder.mappingStyle, newlineScalarStyle: encoder.newlineScalarStyle) } deinit { @@ -218,12 +220,18 @@ extension _Encoder: SingleValueEncodingContainer { func encode(_ value: T) throws where T: YAMLEncodable { assertCanEncodeNewValue() node = value.box() + if let stringValue = value as? (any StringProtocol), stringValue.contains("\n") { + node.scalar?.style = newlineScalarStyle + } } func encode(_ value: T) throws where T: Encodable { assertCanEncodeNewValue() if let encodable = value as? YAMLEncodable { node = encodable.box() + if let stringValue = value as? (any StringProtocol), stringValue.contains("\n") { + node.scalar?.style = newlineScalarStyle + } } else { try value.encode(to: self) } From 67dce297c1fe5f2e4b8e6713149ef28430a38bd9 Mon Sep 17 00:00:00 2001 From: Tejas Sharma Date: Wed, 7 Feb 2024 11:26:52 -0800 Subject: [PATCH 2/6] tests --- Tests/YamsTests/EncoderTests.swift | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Tests/YamsTests/EncoderTests.swift b/Tests/YamsTests/EncoderTests.swift index 2eaa2269..bbda9273 100644 --- a/Tests/YamsTests/EncoderTests.swift +++ b/Tests/YamsTests/EncoderTests.swift @@ -151,6 +151,27 @@ class EncoderTests: XCTestCase { // swiftlint:disable:this type_body_length _testRoundTrip(of: OptionalTopLevelWrapper(url), expectedYAML: expectedYAML) } + func testNewlinesInString() throws { + let expectedYamlLiteral = """ + name: |- + This name + Has new lines + email: test@test.test + + """ + let expectedYamlFolded = """ + name: >- + This name + + Has new lines + email: test@test.test + + """ + let person = Person(name: "This name\nHas new lines", email: "test@test.test") + _testRoundTrip(of: person, with: .init(newLineScalarStyle: .literal), expectedYAML: expectedYamlLiteral) + _testRoundTrip(of: person, with: .init(newLineScalarStyle: .folded), expectedYAML: expectedYamlFolded) + } + func testNumberInString() throws { _testDecode(of: String.self, from: "'10'", expectedValue: "10") _testDecode(of: String.self, from: "'10.5'", expectedValue: "10.5") From db060e8126660ebd44430da6f879e0459c432924 Mon Sep 17 00:00:00 2001 From: Tejas Sharma Date: Fri, 29 Mar 2024 09:58:09 -0700 Subject: [PATCH 3/6] update changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03771500..cd119337 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,9 @@ ##### Enhancements -* None. +* Allow specifying a `newLineScalarStyle` for encoding string scalars with newlines when using `YAMLEncoder` + [Tejas Sharma](https://github.com/tejassharma96) + [#405](https://github.com/jpsim/Yams/issues/405) ##### Bug Fixes From b2ed94e8fc2a46224a67762704d3089b3c9f1b38 Mon Sep 17 00:00:00 2001 From: Tejas Sharma Date: Fri, 29 Mar 2024 12:07:04 -0700 Subject: [PATCH 4/6] swiftlint --- Sources/Yams/Encoder.swift | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Sources/Yams/Encoder.swift b/Sources/Yams/Encoder.swift index 5a68c913..fd17bac3 100644 --- a/Sources/Yams/Encoder.swift +++ b/Sources/Yams/Encoder.swift @@ -125,15 +125,21 @@ private class _ReferencingEncoder: _Encoder { init(referencing encoder: _Encoder, key: CodingKey) { self.encoder = encoder reference = .mapping(key.stringValue) - super.init(userInfo: encoder.userInfo, codingPath: encoder.codingPath + [key], - sequenceStyle: encoder.sequenceStyle, mappingStyle: encoder.mappingStyle, newlineScalarStyle: encoder.newlineScalarStyle) + super.init(userInfo: encoder.userInfo, + codingPath: encoder.codingPath + [key], + sequenceStyle: encoder.sequenceStyle, + mappingStyle: encoder.mappingStyle, + newlineScalarStyle: encoder.newlineScalarStyle) } init(referencing encoder: _Encoder, at index: Int) { self.encoder = encoder reference = .sequence(index) - super.init(userInfo: encoder.userInfo, codingPath: encoder.codingPath + [_YAMLCodingKey(index: index)], - sequenceStyle: encoder.sequenceStyle, mappingStyle: encoder.mappingStyle, newlineScalarStyle: encoder.newlineScalarStyle) + super.init(userInfo: encoder.userInfo, + codingPath: encoder.codingPath + [_YAMLCodingKey(index: index)], + sequenceStyle: encoder.sequenceStyle, + mappingStyle: encoder.mappingStyle, + newlineScalarStyle: encoder.newlineScalarStyle) } deinit { From 6ef2b9e8224bab2cde3a18487e15fd17d2fc324f Mon Sep 17 00:00:00 2001 From: Tejas Sharma Date: Mon, 1 Apr 2024 09:34:54 -0700 Subject: [PATCH 5/6] just use String instead of StringProtocol for pre-5.6 --- Sources/Yams/Encoder.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Sources/Yams/Encoder.swift b/Sources/Yams/Encoder.swift index fd17bac3..d0c23b94 100644 --- a/Sources/Yams/Encoder.swift +++ b/Sources/Yams/Encoder.swift @@ -226,18 +226,30 @@ extension _Encoder: SingleValueEncodingContainer { func encode(_ value: T) throws where T: YAMLEncodable { assertCanEncodeNewValue() node = value.box() +#if swift(>=5.6) if let stringValue = value as? (any StringProtocol), stringValue.contains("\n") { node.scalar?.style = newlineScalarStyle } +#else + if let stringValue = value as? String, stringValue.contains("\n") { + node.scalar?.style = newlineScalarStyle + } +#endif } func encode(_ value: T) throws where T: Encodable { assertCanEncodeNewValue() if let encodable = value as? YAMLEncodable { node = encodable.box() +#if swift(>=5.6) if let stringValue = value as? (any StringProtocol), stringValue.contains("\n") { node.scalar?.style = newlineScalarStyle } +#else + if let stringValue = value as? String, stringValue.contains("\n") { + node.scalar?.style = newlineScalarStyle + } +#endif } else { try value.encode(to: self) } From dcc5b8f32cf44e81e1ac33d967114e3897d76f5a Mon Sep 17 00:00:00 2001 From: Tejas Sharma Date: Wed, 3 Apr 2024 13:04:36 -0700 Subject: [PATCH 6/6] Update to swift 5.7 instead of 5.6 --- Sources/Yams/Encoder.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Yams/Encoder.swift b/Sources/Yams/Encoder.swift index d0c23b94..a17711ee 100644 --- a/Sources/Yams/Encoder.swift +++ b/Sources/Yams/Encoder.swift @@ -226,7 +226,7 @@ extension _Encoder: SingleValueEncodingContainer { func encode(_ value: T) throws where T: YAMLEncodable { assertCanEncodeNewValue() node = value.box() -#if swift(>=5.6) +#if swift(>=5.7) if let stringValue = value as? (any StringProtocol), stringValue.contains("\n") { node.scalar?.style = newlineScalarStyle } @@ -241,7 +241,7 @@ extension _Encoder: SingleValueEncodingContainer { assertCanEncodeNewValue() if let encodable = value as? YAMLEncodable { node = encodable.box() -#if swift(>=5.6) +#if swift(>=5.7) if let stringValue = value as? (any StringProtocol), stringValue.contains("\n") { node.scalar?.style = newlineScalarStyle }