Skip to content

Commit

Permalink
Add an option for encoding scalars with newlines to Emitter.Options (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
tejassharma96 authored Apr 7, 2024
1 parent 0d523df commit 029b612
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 18 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
36 changes: 25 additions & 11 deletions Sources/Yams/Emitter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public func dump<Objects>(
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 {
Expand All @@ -62,7 +63,8 @@ public func dump<Objects>(
version: version,
sortKeys: sortKeys,
sequenceStyle: sequenceStyle,
mappingStyle: mappingStyle
mappingStyle: mappingStyle,
newLineScalarStyle: newLineScalarStyle
)
}

Expand Down Expand Up @@ -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,
Expand All @@ -109,7 +112,8 @@ public func dump(
version: version,
sortKeys: sortKeys,
sequenceStyle: sequenceStyle,
mappingStyle: mappingStyle
mappingStyle: mappingStyle,
newLineScalarStyle: newLineScalarStyle
)
}

Expand Down Expand Up @@ -143,7 +147,8 @@ public func serialize<Nodes>(
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,
Expand All @@ -156,7 +161,8 @@ public func serialize<Nodes>(
version: version,
sortKeys: sortKeys,
sequenceStyle: sequenceStyle,
mappingStyle: mappingStyle
mappingStyle: mappingStyle,
newLineScalarStyle: newLineScalarStyle
)
try emitter.open()
try nodes.forEach(emitter.serialize)
Expand Down Expand Up @@ -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,
Expand All @@ -207,7 +214,8 @@ public func serialize(
version: version,
sortKeys: sortKeys,
sequenceStyle: sequenceStyle,
mappingStyle: mappingStyle
mappingStyle: mappingStyle,
newLineScalarStyle: newLineScalarStyle
)
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -430,6 +443,7 @@ extension Emitter.Options {
self.sortKeys = sortKeys
self.sequenceStyle = sequenceStyle
self.mappingStyle = mappingStyle
self.newLineScalarStyle = newLineScalarStyle
}
}

Expand Down
38 changes: 32 additions & 6 deletions Sources/Yams/Encoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class YAMLEncoder {
public func encode<T: Swift.Encodable>(_ 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)
Expand All @@ -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
Expand All @@ -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<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> {
if canEncodeNewValue {
Expand Down Expand Up @@ -123,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)
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)
super.init(userInfo: encoder.userInfo,
codingPath: encoder.codingPath + [_YAMLCodingKey(index: index)],
sequenceStyle: encoder.sequenceStyle,
mappingStyle: encoder.mappingStyle,
newlineScalarStyle: encoder.newlineScalarStyle)
}

deinit {
Expand Down Expand Up @@ -218,12 +226,30 @@ extension _Encoder: SingleValueEncodingContainer {
func encode<T>(_ value: T) throws where T: YAMLEncodable {
assertCanEncodeNewValue()
node = value.box()
#if swift(>=5.7)
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<T>(_ value: T) throws where T: Encodable {
assertCanEncodeNewValue()
if let encodable = value as? YAMLEncodable {
node = encodable.box()
#if swift(>=5.7)
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)
}
Expand Down
21 changes: 21 additions & 0 deletions Tests/YamsTests/EncoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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: [email protected]
"""
let expectedYamlFolded = """
name: >-
This name
Has new lines
email: [email protected]
"""
let person = Person(name: "This name\nHas new lines", email: "[email protected]")
_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")
Expand Down

0 comments on commit 029b612

Please sign in to comment.