Skip to content

Commit

Permalink
add additional LintingDictionary API per #14 and #27 (#30)
Browse files Browse the repository at this point in the history
* divide up large file

* add additional LintingDictionary API per #14 and #27

* forgot to commit JSON.swift
  • Loading branch information
tayloraswift committed Jul 20, 2022
1 parent 514c836 commit 6a5c716
Show file tree
Hide file tree
Showing 5 changed files with 1,045 additions and 956 deletions.
89 changes: 89 additions & 0 deletions Sources/JSON/JSON.Base10.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
extension JSON
{
/// A namespace for decimal-related functionality.
///
/// This API is used by library functions that are emitted into the client.
/// Most users of ``/swift-json`` should not have to call it directly.
public
enum Base10
{
/// Positive powers of 10, up to [`10_000_000_000_000_000_000`]().
public static
let Exp:[UInt64] =
[
1,
10,
100,

1_000,
10_000,
100_000,

1_000_000,
10_000_000,
100_000_000,

1_000_000_000,
10_000_000_000,
100_000_000_000,

1_000_000_000_000,
10_000_000_000_000,
100_000_000_000_000,

1_000_000_000_000_000,
10_000_000_000_000_000,
100_000_000_000_000_000,

1_000_000_000_000_000_000,
10_000_000_000_000_000_000,
// UInt64.max:
// 18_446_744_073_709_551_615
]
/// Negative powers of 10, down to [`1e-19`]().
public
enum Inverse
{
/// Returns the inverse of the given power of 10.
/// - Parameters:
/// - x: A positive exponent. If `x` is [`2`](), this subscript
/// will return [`1e-2`]().
/// - _: A ``BinaryFloatingPoint`` type.
@inlinable public static
subscript<T>(x:Int, as _:T.Type) -> T
where T:BinaryFloatingPoint
{
let inverses:[T] =
[
1,
1e-1,
1e-2,

1e-3,
1e-4,
1e-5,

1e-6,
1e-7,
1e-8,

1e-9,
1e-10,
1e-11,

1e-12,
1e-13,
1e-14,

1e-15,
1e-16,
1e-17,

1e-18,
1e-19,
]
return inverses[x]
}
}
}
}
143 changes: 112 additions & 31 deletions Sources/JSON/JSON.LintingDictionary.swift
Original file line number Diff line number Diff line change
@@ -1,33 +1,50 @@
extension JSON
{
@available(*, unavailable, message: "use one of lint(discarding:_:) or lint(whitelisting:_:)")
public
func lint<S, T>(_:S, _:(inout LintingDictionary) throws -> T) throws -> T
where S:Sequence, S.Element == String
{
preconditionFailure()
}

@inlinable public
func lint<T>(_ body:(inout LintingDictionary) throws -> T) throws -> T
{
try self.lint([], body)
try self.lint(whitelisting: EmptyCollection<String>.init(), body)
}
@inlinable public
func lint<S, T>(_ ignored:S, _ body:(inout LintingDictionary) throws -> T) throws -> T
where S:Sequence, S.Element == String
func lint<Discards, T>(discarding discards:Discards,
_ body:(inout LintingDictionary) throws -> T) throws -> T
where Discards:Sequence, Discards.Element == String
{
var dictionary:LintingDictionary
do
try self.lint(whitelisting: EmptyCollection<String>.init())
{
// this `do` block is not for error catching, it is for ending
// the lifetime of `items` before passing a copy of it to `body`
var items:[String: Self] = try self.as([String: Self].self) { $1 }
for key:String in ignored
for key:String in discards
{
items.removeValue(forKey: key)
let _:JSON = try $0.remove(key)
}
dictionary = .init(items)
return try body(&$0)
}
}
@inlinable public
func lint<Whitelist, T>(whitelisting whitelist:Whitelist,
_ body:(inout LintingDictionary) throws -> T) throws -> T
where Whitelist:Sequence, Whitelist.Element == String
{
var dictionary:LintingDictionary =
.init(try self.as([String: Self].self) { $1 })
let result:T = try body(&dictionary)
for key:String in whitelist
{
let _:JSON? = dictionary.pop(key)
}
let value:T = try body(&dictionary)
guard dictionary.items.isEmpty
else
{
throw LintingError.init(unused: dictionary.items)
}
return value
return result
}

@frozen public
Expand All @@ -42,14 +59,51 @@ extension JSON
self.items = items
}

/// Returns the variant value for the given key if it exists, or [`nil`]()
/// otherwise.
///
/// Use the ``pop(_:_:)`` method to generate a more-detailed error trace
/// if decoding fails later.
@inlinable public mutating
func remove<T>(_ key:String, _ body:(JSON) throws -> T) throws -> T
func pop(_ key:String) -> JSON?
{
guard let value:JSON = self.items.removeValue(forKey: key)
self.items.removeValue(forKey: key)
}
/// Returns the variant value for the given key.
///
/// Use the ``remove(_:_:)`` method to generate a more-detailed error trace
/// if decoding fails later.
///
/// > Throws:
/// A ``JSON//PrimitiveError.undefined(key:in:)`` if the key does
/// not exist.
@inlinable public mutating
func remove(_ key:String) throws -> JSON
{
if let value:JSON = self.pop(key)
{
return value
}
else
{
throw PrimitiveError.undefined(key: key, in: self.items)
}
}
/// Finds the variant value for the given key if it exists, and passes
/// it to the given closure for further decoding. Returns the result of
/// the closure, or [`nil`]() if the key does not exist.
///
/// > Throws:
/// A ``JSON//RecursiveError.dictionary(underlying:in:)`` if an error
/// was thrown from within the given closure.
@inlinable public mutating
func pop<T>(_ key:String, _ body:(JSON) throws -> T) rethrows -> T?
{
guard let value:JSON = self.pop(key)
else
{
return nil
}
do
{
#if swift(>=5.7)
Expand All @@ -63,14 +117,20 @@ extension JSON
throw RecursiveError.dictionary(underlying: error, in: key)
}
}
/// Finds the variant value for the given key and passes
/// it to the given closure for further decoding, returning its result.
///
/// > Throws:
/// A ``JSON//PrimitiveError.undefined(key:in:)`` if the key does
/// not exist, or a ``JSON//RecursiveError.dictionary(underlying:in:)``
/// if an error was thrown from within the given closure.
@inlinable public mutating
func pop<T>(_ key:String, _ body:(JSON) throws -> T) throws -> T?
func remove<T>(_ key:String, _ body:(JSON) throws -> T) throws -> T
{
guard let value:JSON = self.items.removeValue(forKey: key)
else
{
return nil
}
// we cannot *quite* shove this into the `do` block, because we
// do not want to throw a ``RecursiveError`` just because the key
// was not found.
let value:JSON = try self.remove(key)
do
{
#if swift(>=5.7)
Expand All @@ -87,9 +147,30 @@ extension JSON

// arrays
@inlinable public mutating
func remove<T>(_ key:String, as _:[JSON].Type = [JSON].self, _ body:([JSON]) throws -> T) throws -> T
func pop(_ key:String, as _:[JSON].Type = [JSON].self) throws -> [JSON]?
{
try self.remove(key)
try self.pop(key, as: [JSON].self) { $0 }
}
@inlinable public mutating
func pop(_ key:String, as _:[JSON]?.Type = [JSON]?.self) throws -> [JSON]?
{
try self.pop(key, as: [JSON]?.self) { $0 }
}
@inlinable public mutating
func remove(_ key:String, as _:[JSON].Type = [JSON].self) throws -> [JSON]
{
try self.remove(key, as: [JSON].self) { $0 }
}
@inlinable public mutating
func remove(_ key:String, as _:[JSON]?.Type = [JSON]?.self) throws -> [JSON]?
{
try self.remove(key, as: [JSON]?.self) { $0 }
}

@inlinable public mutating
func pop<T>(_ key:String, as _:[JSON].Type, _ body:([JSON]) throws -> T) throws -> T?
{
try self.pop(key)
{
let array:[JSON] = try $0.as([JSON].self)
do
Expand All @@ -107,9 +188,9 @@ extension JSON
}
}
@inlinable public mutating
func remove<T>(_ key:String, as _:[JSON]?.Type = [JSON]?.self, _ body:([JSON]) throws -> T) throws -> T?
func pop<T>(_ key:String, as _:[JSON]?.Type, _ body:([JSON]) throws -> T) throws -> T?
{
try self.remove(key)
try self.pop(key)
{
guard let array:[JSON] = try $0.as([JSON]?.self)
else
Expand All @@ -128,12 +209,12 @@ extension JSON
{
throw RecursiveError.array(underlying: error)
}
}
} ?? nil
}
@inlinable public mutating
func pop<T>(_ key:String, as _:[JSON].Type, _ body:([JSON]) throws -> T) throws -> T?
func remove<T>(_ key:String, as _:[JSON].Type = [JSON].self, _ body:([JSON]) throws -> T) throws -> T
{
try self.pop(key)
try self.remove(key)
{
let array:[JSON] = try $0.as([JSON].self)
do
Expand All @@ -151,9 +232,9 @@ extension JSON
}
}
@inlinable public mutating
func pop<T>(_ key:String, as _:[JSON]?.Type, _ body:([JSON]) throws -> T) throws -> T?
func remove<T>(_ key:String, as _:[JSON]?.Type = [JSON]?.self, _ body:([JSON]) throws -> T) throws -> T?
{
try self.pop(key)
try self.remove(key)
{
guard let array:[JSON] = try $0.as([JSON]?.self)
else
Expand All @@ -172,7 +253,7 @@ extension JSON
{
throw RecursiveError.array(underlying: error)
}
} ?? nil
}
}

// null
Expand Down

0 comments on commit 6a5c716

Please sign in to comment.