Skip to content

Commit

Permalink
Faster implementation of CustomStringConvertible for YAML
Browse files Browse the repository at this point in the history
  • Loading branch information
kdubb committed Jun 18, 2024
1 parent 660e33e commit c4bfe34
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 13 deletions.
51 changes: 46 additions & 5 deletions Sources/PotentYAML/YAML.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import BigInt
import Foundation
import OrderedCollections
import PotentCodables


Expand All @@ -34,7 +35,7 @@ import PotentCodables
@dynamicMemberLookup
public enum YAML {

public struct Tag: RawRepresentable, Equatable, Hashable {
public struct Tag: RawRepresentable, Equatable, Hashable, CustomStringConvertible {

public let rawValue: String

Expand All @@ -59,6 +60,8 @@ public enum YAML {

public static let seq = Tag("tag:yaml.org,2002:seq")
public static let map = Tag("tag:yaml.org,2002:map")

public var description: String { "!\(rawValue)" }
}

public typealias Anchor = String
Expand Down Expand Up @@ -246,6 +249,15 @@ public enum YAML {
return nil
}

public static func mapping(
_ mapping: OrderedDictionary<YAML, YAML>,
style: CollectionStyle = .any,
tag: Tag? = nil,
anchor: Anchor? = nil
) -> YAML {
return .mapping(mapping.map { .init(key: $0.key, value: $0.value) }, style: style, tag: tag, anchor: anchor)
}

public var stringValue: String? {
guard case .string(let value, _, _, _) = self else { return nil }
return value
Expand Down Expand Up @@ -341,14 +353,43 @@ extension YAML: Value {
extension YAML: CustomStringConvertible {

public var description: String {
let schema: YAMLSchema = .core
var output = ""

do {
try YAMLWriter.write([self]) { output += $0 ?? "" }
func append<T: CustomStringConvertible>(_ anchor: String?, _ tag: Tag?, _ value: T) {
if let anchor {
output += "&\(anchor) "
}
if let tag {
output += "\(tag) "
}
output += value.description
}
catch {
return "Invalid YAML: \(error)"

switch self {
case .alias(let value):
output += "*\(value)"
case .string(let value, style: let style, tag: let tag, anchor: let anchor):
switch style {
case .doubleQuoted:
append(anchor, tag, #""\#(value)""#)
case .singleQuoted:
append(anchor, tag, "'\(value)'")
case .folded, .literal, .plain, .any:
append(anchor, tag, schema.requiresQuotes(for: value) ? #""\#(value)""# : value)
}
case .bool(let value, anchor: let anchor):
append(anchor, nil, value)
case .null(anchor: let anchor):
append(anchor, nil, "null")
case .integer(let value, anchor: let anchor), .float(let value, anchor: let anchor):
append(anchor, nil, value.value)
case .mapping(let value, style: _, tag: let tag, anchor: let anchor):
append(anchor, tag, "{\(value.map { "\($0.key.description): \($0.value.description)" }.joined(separator: ", "))}")
case .sequence(let value, style: _, tag: let tag, anchor: let anchor):
append(anchor, tag, "[\(value.map(\.description).joined(separator: ", "))]")
}

return output
}

Expand Down
5 changes: 1 addition & 4 deletions Tests/YAMLOrderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,7 @@ class YAMLOrderTests: XCTestCase {
XCTAssertEqual(
yaml.description,
"""
c: 1
a: 2
b: 3
{c: 1, a: 2, b: 3}
"""
)
}
Expand Down
55 changes: 51 additions & 4 deletions Tests/YAMLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,56 @@ class YAMLTests: XCTestCase {
XCTAssertEqual(object, try YAMLSerialization.yaml(from: yaml))
}

func testDescriptionFormat() throws {

XCTAssertEqual(YAML.alias("test").description, "*test")

XCTAssertEqual(YAML.null().description, "null")
XCTAssertEqual(YAML.null(anchor: "test").description, "&test null")

XCTAssertEqual(YAML.string("my-string", style: .plain).description,
"my-string")
XCTAssertEqual(YAML.string("my-string", style: .doubleQuoted).description,
#""my-string""#)
XCTAssertEqual(YAML.string("my-string", style: .singleQuoted).description,
#"'my-string'"#)
XCTAssertEqual(YAML.string("my-string", style: .plain, anchor: "test").description,
"&test my-string")
XCTAssertEqual(YAML.string("my-string", style: .plain, tag: .str).description,
"!tag:yaml.org,2002:str my-string")
XCTAssertEqual(YAML.string("my-string", style: .plain, tag: .str, anchor: "test").description,
"&test !tag:yaml.org,2002:str my-string")

XCTAssertEqual(YAML.integer(123).description, "123")
XCTAssertEqual(YAML.integer(123, anchor: "test").description, "&test 123")
XCTAssertEqual(YAML.integer(123, anchor: "test").description, "&test 123")

XCTAssertEqual(YAML.float(1.23).description, "1.23")
XCTAssertEqual(YAML.float(1.23, anchor: "test").description, "&test 1.23")

XCTAssertEqual(YAML.bool(false).description, "false")
XCTAssertEqual(YAML.bool(true, anchor: "test").description, "&test true")

XCTAssertEqual(YAML.sequence([1, 2, 3]).description,
"[1, 2, 3]")
XCTAssertEqual(YAML.sequence([1, 2, 3], anchor: "test").description,
"&test [1, 2, 3]")
XCTAssertEqual(YAML.sequence([1, 2, 3], tag: .seq).description,
"!tag:yaml.org,2002:seq [1, 2, 3]")
XCTAssertEqual(YAML.sequence([1, 2, 3], tag: .seq, anchor: "test").description,
"&test !tag:yaml.org,2002:seq [1, 2, 3]")

XCTAssertEqual(YAML.mapping(["c": 1, "a": 2, "b": 3]).description,
"{c: 1, a: 2, b: 3}")
XCTAssertEqual(YAML.mapping(["c": 1, "a": 2, "b": 3], anchor: "test").description,
"&test {c: 1, a: 2, b: 3}")
XCTAssertEqual(YAML.mapping(["c": 1, "a": 2, "b": 3], tag: .map).description,
"!tag:yaml.org,2002:map {c: 1, a: 2, b: 3}")
XCTAssertEqual(YAML.mapping(["c": 1, "a": 2, "b": 3], tag: .map, anchor: "test").description,
"&test !tag:yaml.org,2002:map {c: 1, a: 2, b: 3}")

}

func testDescriptionOrder() throws {

let yaml: YAML = [
Expand All @@ -224,10 +274,7 @@ class YAMLTests: XCTestCase {
XCTAssertEqual(
yaml.description,
"""
c: 1
a: 2
b: 3
{c: 1, a: 2, b: 3}
"""
)
}
Expand Down

0 comments on commit c4bfe34

Please sign in to comment.