Skip to content

Commit

Permalink
unnest JSON.Rule.Root to JSON.Rule and add LintingDictionary snippet (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
tayloraswift committed Jul 31, 2022
1 parent a7e049b commit 59d34c5
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 33 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ The `JSON` module in `swift-json` enables you to express JSON parsing tasks as *

To parse a complete JSON message, use the [`JSON.Rule<Location>.Root`](https://swiftinit.org/reference/swift-json/json/json/rule/root) parsing rule:

> [`basic-decoding.swift`](Snippets/basic-decoding.swift)
> [`BasicDecoding.swift`](Snippets/BasicDecoding.swift)
```swift
import JSON
Expand Down Expand Up @@ -69,7 +69,7 @@ print(response)
```

```text
$ .build/release/basic-decoding
$ .build/release/BasicDecoding
Response(success: true, value: Decimal(units: 1, places: 1))
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ let string:String =
{"success":true,"value":0.1}
"""
let decoder:JSON = try Grammar.parse(string.utf8,
as: JSON.Rule<String.Index>.Root.self)
as: JSON.Rule<String.Index>.self)
let response:Response = try .init(from: decoder)

print(response)
Expand All @@ -28,7 +28,7 @@ let invalid:String =
do
{
let _:JSON = try Grammar.parse(diagnosing: invalid.utf8,
as: JSON.Rule<String.Index>.Root.self)
as: JSON.Rule<String.Index>.self)
}
catch let error as ParsingError<String.Index>
{
Expand Down
113 changes: 113 additions & 0 deletions Snippets/LintingDictionary.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import JSON

// ``JSON//LintingDictionary`` provides a lightweight functional interface
// for decoding JSON messages with built-in error handling.

// snippet.market-enum-definition
enum Market:String
{
case spot
case future
}
// snippet.main
func decode(message:String) throws ->
(
name:String,
market:Market,
isPerpetual:Bool
)
{
// snippet.parse
let json:JSON = try Grammar.parse(message.utf8,
as: JSON.Rule<String.Index>.self)
// snippet.decode
return try json.lint
{
try $0.remove("market")
{
try $0.lint
{
// snippet.decode-string
let name:String = try $0.remove("name", as: String.self)


// snippet.decode-enum
let market:Market = try $0.remove("type")
{
try $0.as(cases: Market.self)
}

// snippet.decode-conditional
let isPerpetual:Bool
if case .future = market
{
isPerpetual = try $0.pop("perpetual", as: Bool.self) ?? false
}
else
{
isPerpetual = false
}
// snippet.return
return (name, market, isPerpetual)
}
}
}
}
// snippet.example-success
print(try decode(message:
"""
{
"market":
{
"name": "BTC-PERP",
"type": "future",
"perpetual": true
}
}
"""))

// snippet.example-failure
print(try decode(message:
"""
{
"market":
{
"name": "BTC-PERP",
"type": "spot",
"perpetual": true
}
}
"""))

// snippet.hide
extension JSON.LintingDictionary
{
private mutating
func lintArrays(_ key:String, as _:[JSON].Type, _ body:([JSON]) throws -> ()) throws
{
// snippet.pop-array-equivalence
try self.pop(key)
{
try body(try $0.as([JSON].self))
}
// snippet.hide
// snippet.pop-array-or-null-equivalence
try self.pop(key)
{
try $0.as([JSON]?.self).map(body)
} ?? nil
// snippet.hide
// snippet.remove-array-equivalence
try self.remove(key)
{
try body(try $0.as([JSON].self))
}
// snippet.hide
// snippet.remove-array-or-nullequivalence
try self.remove(key)
{
try $0.as([JSON]?.self).map(body)
}
// snippet.hide
}
}
59 changes: 30 additions & 29 deletions Sources/JSON/JSON.Rule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
extension JSON
{
/// @import(Grammar)
/// A generic context for structured parsing rules.
/// Matches a complete message; either an ``JSON/Rule//Array`` or an ``JSON/Rule//Object``.
///
/// All of the parsing rules in this library are defined at the UTF-8 level.
///
/// To parse *any* JSON value, including fragment values, use the ``JSON/Rule//Value``
/// rule instead.
///
/// You can parse JSON expressions from any ``Collection`` with an
/// ``Collection//Element`` type of ``UInt8``. For example, you can parse
/// a ``String`` through its ``String//UTF8View``.
Expand Down Expand Up @@ -42,7 +45,7 @@ extension JSON
/// even when applied to third-party collection types, like
/// ``/swift-nio/NIOCore/ByteBufferView``.
public
enum Rule<Location>
enum Rule<Location>
{
/// ASCII terminals.
public
Expand All @@ -55,7 +58,7 @@ extension JSON
typealias DecimalDigit<T> = Grammar.DecimalDigit<Location, UInt8, T> where T:BinaryInteger
}
}
extension JSON.Rule
extension JSON.Rule:ParsingRule
{
/// A literal `null` expression.
public
Expand Down Expand Up @@ -111,32 +114,7 @@ extension JSON.Rule
public
typealias False = JSON.Rule<Location>.False
}

/// Matches a complete message; either an ``JSON/Rule//Array`` or an ``JSON/Rule//Object``.
///
/// To parse *any* JSON value, including fragment values, use the ``JSON/Rule//Value``
/// rule instead.
public
enum Root:ParsingRule
{
public
typealias Terminal = UInt8
@inlinable public static
func parse<Diagnostics>(_ input:inout ParsingInput<Diagnostics>) throws -> JSON
where Diagnostics:ParsingDiagnostics,
Diagnostics.Source.Index == Location,
Diagnostics.Source.Element == Terminal
{
if let items:[(key:String, value:JSON)] = input.parse(as: Object?.self)
{
return .object(items)
}
else
{
return .array(try input.parse(as: Array.self))
}
}
}

/// Matches any value, including fragment values.
///
/// Only use this if you are doing manual JSON parsing. Most web services
Expand Down Expand Up @@ -579,4 +557,27 @@ extension JSON.Rule
return items
}
}

@available(*, deprecated, renamed: "JSON.Rule")
public
typealias Root = JSON.Rule<Location>

public
typealias Terminal = UInt8

@inlinable public static
func parse<Diagnostics>(_ input:inout ParsingInput<Diagnostics>) throws -> JSON
where Diagnostics:ParsingDiagnostics,
Diagnostics.Source.Index == Location,
Diagnostics.Source.Element == Terminal
{
if let items:[(key:String, value:JSON)] = input.parse(as: Object?.self)
{
return .object(items)
}
else
{
return .array(try input.parse(as: Array.self))
}
}
}

0 comments on commit 59d34c5

Please sign in to comment.