Skip to content

Commit 53fa3db

Browse files
committed
add some common sense convenience constructors for JSON.Node
1 parent ff2b1e1 commit 53fa3db

5 files changed

Lines changed: 45 additions & 69 deletions

File tree

Sources/JQTests/AccessArrayTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,10 @@ import Testing
145145

146146
var nested: JSON.Node = [[[false, true], [true, false]]]
147147
try nested[0][1] & {
148-
$0 = .number(.init(1))
148+
$0 = .number(1)
149149
}
150150

151-
#expect("\(nested)" == "\([[[false, true], .number(.init(1))]] as JSON.Node)")
151+
#expect("\(nested)" == "\([[[false, true], .number(1)]] as JSON.Node)")
152152

153153
try nested[0][] & {
154154
$0 = [true]

Sources/JQTests/AccessObjectTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,16 +71,16 @@ import Testing
7171
@Test static func Modify() throws {
7272
var node: JSON.Node = ["a": ["x": false, "y": true]]
7373
try node["a"]["y"] & {
74-
$0 = .number(.init(1))
74+
$0 = .number(1)
7575
}
7676

77-
#expect("\(node)" == "\(["a": ["x": false, "y": .number(.init(1))]] as JSON.Node)")
77+
#expect("\(node)" == "\(["a": ["x": false, "y": .number(1)]] as JSON.Node)")
7878

7979
try node["a"] & {
80-
$0 = .number(.init(1))
80+
$0 = .number(1)
8181
}
8282

83-
#expect("\(node)" == "\(["a": .number(.init(1))] as JSON.Node)")
83+
#expect("\(node)" == "\(["a": .number(1)] as JSON.Node)")
8484

8585
try node["a"] & {
8686
$0 = [true, false]

Sources/JSONAST/JSON.Node.swift

Lines changed: 36 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -20,64 +20,24 @@ extension JSON {
2020
}
2121
}
2222
extension JSON.Node {
23-
// TODO: optimize this, it should operate at the utf8 level, and be @inlinable
23+
/// A shorthand for constructing the payload of ``string(_:) (Literal<String>)``.
24+
@inlinable public static func string(_ unescaped: consuming String) -> Self {
25+
.string(JSON.Literal<String>.init(unescaped))
26+
}
2427

25-
/// Escapes and formats a string as a JSON string literal, including the
26-
/// beginning and ending quote characters. This function is used for debug reflection only;
27-
/// it is less efficient than the UTF-8 escaping implementation used by the encoder.
28-
///
29-
/// - Parameters:
30-
/// - string: A string to escape.
31-
/// - Returns: A string literal, which includes the `""` delimiters.
32-
///
33-
/// This function escapes the following characters: `"`, `\`, `\b`, `\t`, `\n`,
34-
/// `\f`, and `\r`. It does not escape forward slashes (`/`).
35-
///
36-
/// JSON string literals may contain unicode characters, even after escaping.
37-
/// Do not assume the output of this function is ASCII.
38-
///
39-
/// > Important: This function should *not* be called on an input to the ``string(_:)``
40-
/// case constructor. The library performs string escaping lazily; calling this
41-
/// function explicitly will double-escape the input.
42-
// static
43-
// func escape<S>(_ string:S) -> String where S:StringProtocol
44-
// {
45-
// var escaped:String = "\""
46-
// for character:Character in string
47-
// {
48-
// switch character
49-
// {
50-
// case "\"": escaped += "\\\""
51-
// case "\\": escaped += "\\\\"
52-
// // slash escape is not mandatory, and does not improve legibility
53-
// // case "/": escaped += "\\/"
54-
// case "\u{08}": escaped += "\\b"
55-
// case "\u{09}": escaped += "\\t"
56-
// case "\u{0A}": escaped += "\\n"
57-
// case "\u{0C}": escaped += "\\f"
58-
// case "\u{0D}": escaped += "\\r"
59-
// default: escaped.append(character)
60-
// }
61-
// }
62-
// escaped += "\""
63-
// return escaped
64-
// }
65-
}
66-
extension JSON.Node: CustomStringConvertible {
67-
/// Returns this value serialized as a minified string.
68-
///
69-
/// Reparsing and reserializing this string is guaranteed to return the
70-
/// same string.
71-
public var description: String {
72-
switch self {
73-
case .null: "null"
74-
case .bool(true): "true"
75-
case .bool(false): "false"
76-
case .string(let self): .init(self)
77-
case .number(let self): "\(self)"
78-
case .array(let self): "\(self)"
79-
case .object(let self): "\(self)"
80-
}
28+
/// A shorthand for constructing the payload of ``number(_:) (Number)``.
29+
@inlinable public static func number<T>(_ value: T) -> Self where T: UnsignedInteger {
30+
.number(JSON.Number.init(value))
31+
}
32+
/// A shorthand for constructing the payload of ``number(_:) (Number)``.
33+
@inlinable public static func number<T>(_ value: T) -> Self where T: SignedInteger {
34+
.number(JSON.Number.init(value))
35+
}
36+
/// A shorthand for constructing the payload of ``number(_:) (Number)``.
37+
@inlinable public static func number<T>(
38+
_ value: T
39+
) -> Self where T: BinaryFloatingPoint & LosslessStringConvertible {
40+
.number(JSON.Number.init(value))
8141
}
8242
}
8343
extension JSON.Node: ExpressibleByDictionaryLiteral {
@@ -90,17 +50,33 @@ extension JSON.Node: ExpressibleByArrayLiteral {
9050
self = .array(.init(arrayLiteral))
9151
}
9252
}
93-
extension JSON.Node: ExpressibleByStringLiteral {
53+
extension JSON.Node: ExpressibleByStringLiteral, ExpressibleByStringInterpolation {
9454
@inlinable public init(stringLiteral: String) {
95-
self = .string(JSON.Literal<String>.init(stringLiteral))
55+
self = .string(stringLiteral)
9656
}
9757
}
9858
extension JSON.Node: ExpressibleByBooleanLiteral {
9959
@inlinable public init(booleanLiteral: Bool) {
10060
self = .bool(booleanLiteral)
10161
}
10262
}
103-
63+
extension JSON.Node: CustomStringConvertible {
64+
/// Returns this value serialized as a minified string.
65+
///
66+
/// Reparsing and reserializing this string is guaranteed to return the
67+
/// same string.
68+
public var description: String {
69+
switch self {
70+
case .null: "null"
71+
case .bool(true): "true"
72+
case .bool(false): "false"
73+
case .string(let self): .init(self)
74+
case .number(let self): "\(self)"
75+
case .array(let self): "\(self)"
76+
case .object(let self): "\(self)"
77+
}
78+
}
79+
}
10480
extension JSON.Node {
10581
/// Promotes a `nil` result to a thrown ``TypecastError``.
10682
///

Sources/JSONParsing/Rules/JSON.NodeRule.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ extension JSON.NodeRule: ParsingRule {
1818
return .number(number)
1919
} else if
2020
let string: String = input.parse(as: JSON.StringRule<Location>?.self) {
21-
return .string(JSON.Literal<String>.init(string))
21+
return .string(string)
2222
} else if
2323
let items: [(JSON.Key, JSON.Node)] = input.parse(as: Object?.self) {
2424
return .object(.init(items))

Sources/JSONTests/IntegerOverflow.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ extension IntegerOverflow {
7878
where Signed: SignedInteger & JSONDecodable {
7979
let field: JSON.FieldDecoder<Never?> = .init(
8080
key: nil,
81-
value: .number(.init(value))
81+
value: .number(value)
8282
)
8383

8484
#expect(throws: JSON.DecodingError<Never?>.self) {
@@ -91,7 +91,7 @@ extension IntegerOverflow {
9191
) throws where Signed: SignedInteger & JSONDecodable {
9292
let field: JSON.FieldDecoder<Never?> = .init(
9393
key: nil,
94-
value: .number(.init(value))
94+
value: .number(value)
9595
)
9696

9797
#expect(try field.decode() == value)

0 commit comments

Comments
 (0)