Custom types with ExplorerValue
This commit is contained in:
parent
723bb2d229
commit
2eecb0807e
|
@ -11,6 +11,7 @@ import Foundation
|
|||
/// - note: Default implementation provided for types conforming to `Encodable`
|
||||
public protocol ExplorerValueRepresentable {
|
||||
|
||||
/// Convert `self` to an ``ExplorerValue``
|
||||
func explorerValue() throws -> ExplorerValue
|
||||
}
|
||||
|
||||
|
@ -18,10 +19,11 @@ public protocol ExplorerValueRepresentable {
|
|||
/// - note: Default implementation provided for types conforming to `Decodable`
|
||||
public protocol ExplorerValueCreatable {
|
||||
|
||||
/// Instantiate a new value from an ``ExplorerValue``
|
||||
init(from explorerValue: ExplorerValue) throws
|
||||
}
|
||||
|
||||
/// Can be represented as and instantiated from as `ExplorerValue`
|
||||
/// Can be represented *as* and instantiated *from* an `ExplorerValue`
|
||||
/// - note: Default implementation provided for types conforming to `Codable`
|
||||
public typealias ExplorerValueConvertible = ExplorerValueRepresentable & ExplorerValueCreatable
|
||||
|
||||
|
|
|
@ -125,20 +125,20 @@ public extension PathExplorer {
|
|||
/// Add a value at the given path.
|
||||
///
|
||||
/// ### Appending
|
||||
/// To add a key at the end of an array, specify the `PathElement.count`
|
||||
/// To add a key at the end of an array, specify ``PathElement/count``
|
||||
mutating func add(_ value: ExplorerValue, at path: PathElement...) throws { try add(value, at: Path(path)) }
|
||||
|
||||
/// Add a value at the given path.
|
||||
///
|
||||
/// ### Appending
|
||||
/// To add a key at the end of an array, specify the `PathElement.count`
|
||||
/// To add a key at the end of an array, specify ``PathElement/count``
|
||||
/// - Throws: If the `newValue.explorerValue` function fails
|
||||
mutating func add(_ value: ExplorerValueRepresentable, at path: Path) throws { try add(value.explorerValue(), at: path) }
|
||||
|
||||
/// Add a value at the given path.
|
||||
///
|
||||
/// ### Appending
|
||||
/// To add a key at the end of an array, specify the `PathElement.count`
|
||||
/// To add a key at the end of an array, specify ``PathElement/count``
|
||||
/// - Throws: If the `newValue.explorerValue()` function fails
|
||||
mutating func add(_ value: ExplorerValueRepresentable, at path: PathElement...) throws { try add(value.explorerValue(), at: Path(path)) }
|
||||
|
||||
|
@ -147,23 +147,20 @@ public extension PathExplorer {
|
|||
/// Add a value at the given path, and return a new modified `PathExplorer`
|
||||
///
|
||||
/// ### Appending
|
||||
/// To add a key at the end of an array, specify the `PathElement.count`
|
||||
///
|
||||
/// ### Non-existing key
|
||||
/// Any non existing key encountered in the path will be created.
|
||||
/// To add a key at the end of an array, specify ``PathElement/count``
|
||||
func adding(_ value: ExplorerValue, at path: PathElement...) throws -> Self { try adding(value, at: Path(path)) }
|
||||
|
||||
/// Add a value at the given path, and return a new modified `PathExplorer`
|
||||
///
|
||||
/// ### Appending
|
||||
/// To add a key at the end of an array, specify the `PathElement.count`
|
||||
/// To add a key at the end of an array, specify ``PathElement/count``
|
||||
/// - Throws: If the `newValue.explorerValue()` function fails
|
||||
func adding(_ value: ExplorerValueRepresentable, at path: Path) throws -> Self { try adding(value.explorerValue(), at: path) }
|
||||
|
||||
/// Add a value at the given path, and return a new modified `PathExplorer`
|
||||
///
|
||||
/// ### Appending
|
||||
/// To add a key at the end of an array, specify the `PathElement.count`
|
||||
/// To add a key at the end of an array, specify ``PathElement/count``
|
||||
/// - Throws: If the `newValue.explorerValue()` function fails
|
||||
func adding(_ value: ExplorerValueRepresentable, at path: PathElement...) throws -> Self { try adding(value.explorerValue(), at: Path(path)) }
|
||||
}
|
||||
|
|
|
@ -68,71 +68,34 @@ where
|
|||
// MARK: - Get
|
||||
|
||||
/// Get the key at the given path
|
||||
///
|
||||
/// #### Negative index
|
||||
/// It's possible to specify a negative index to target the last nth element of an array.
|
||||
/// For example, -1 targets the last element and -3 the last 3rd element.
|
||||
/// - Throws: If the path is invalid (e.g. a key does not exist in a dictionary, or indicating an index on a non-array key)
|
||||
func get(_ path: Path) throws -> Self
|
||||
|
||||
// MARK: - Set
|
||||
|
||||
/// Set the value of the key at the given path
|
||||
///
|
||||
/// #### Negative index
|
||||
/// It's possible to specify a negative index to target the last nth element of an array.
|
||||
/// For example, -1 targets the last element and -3 the last 3rd element.
|
||||
///
|
||||
/// - Throws: If the path is invalid (e.g. a key does not exist in a dictionary, or indicating an index on a non-array key)
|
||||
mutating func set(_ path: Path, to newValue: ExplorerValue) throws
|
||||
|
||||
/// Set the value of the key at the given path and returns a new modified `PathExplorer`
|
||||
///
|
||||
/// #### Negative index
|
||||
/// It's possible to specify a negative index to target the last nth element of an array.
|
||||
/// For example, -1 targets the last element and -3 the last 3rd element.
|
||||
///
|
||||
/// - Throws: If the path is invalid (e.g. a key does not exist in a dictionary, or indicating an index on a non-array key)
|
||||
func setting(_ path: Path, to newValue: ExplorerValue) throws -> Self
|
||||
|
||||
// MARK: - Set key name
|
||||
|
||||
/// Set the name of the key at the given path
|
||||
///
|
||||
/// #### Negative index
|
||||
/// It's possible to specify a negative index to target the last nth element of an array.
|
||||
/// For example, -1 targets the last element and -3 the last 3rd element.
|
||||
///
|
||||
/// - Throws: If the path is invalid (e.g. a key does not exist in a dictionary)
|
||||
mutating func set(_ path: Path, keyNameTo newKeyName: String) throws
|
||||
|
||||
/// Set the name of the key at the given path, and return a new modified `PathExplorer`
|
||||
///
|
||||
/// #### Negative index
|
||||
/// It's possible to specify a negative index to target the last nth element of an array.
|
||||
/// For example, -1 targets the last element and -3 the last 3rd element.
|
||||
///
|
||||
/// - Throws: If the path is invalid (e.g. a key does not exist in a dictionary)
|
||||
func setting(_ path: Path, keyNameTo keyName: String) throws -> Self
|
||||
|
||||
// MARK: - Delete
|
||||
|
||||
/// Delete the key at the given path.
|
||||
///
|
||||
/// #### Negative index
|
||||
/// It's possible to specify a negative index to target the last nth element of an array.
|
||||
/// For example, -1 targets the last element and -3 the last 3rd element.
|
||||
///
|
||||
/// - parameter deleteIfEmpty: When `true`, the dictionary or array holding the value will be deleted too if empty after the key deletion. Default: `false`
|
||||
/// - Throws: If the path is invalid (e.g. a key does not exist in a dictionary, or indicating an index on a non-array key)
|
||||
mutating func delete(_ path: Path, deleteIfEmpty: Bool) throws
|
||||
|
||||
/// Delete the key at the given path and return a new modified `PathExplorer`
|
||||
///
|
||||
/// #### Negative index
|
||||
/// It's possible to specify a negative index to target the last nth element of an array.
|
||||
/// For example, -1 targets the last element and -3 the last 3rd element.
|
||||
///
|
||||
/// - parameter deleteIfEmpty: When `true`, the dictionary or array holding the value will be deleted too if empty after the key deletion. Default: `false`
|
||||
/// - Throws: If the path is invalid (e.g. a key does not exist in a dictionary, or indicating an index on a non-array key)
|
||||
func deleting(_ path: Path, deleteIfEmpty: Bool) throws -> Self
|
||||
|
@ -141,25 +104,14 @@ where
|
|||
|
||||
/// Add a value at the given path.
|
||||
///
|
||||
/// #### Negative index
|
||||
/// It's possible to specify a negative index to target the last nth element.
|
||||
/// For example, -1 targets the last element and -3 the last 3rd element.
|
||||
///
|
||||
/// #### Appending
|
||||
/// To add a key at the end of an array, specify the `PathElement.count`
|
||||
/// To add a key at the end of an array, specify ``PathElement/count``
|
||||
mutating func add(_ value: ExplorerValue, at path: Path) throws
|
||||
|
||||
/// Add a value at the given path, and return a new modified `PathExplorer`
|
||||
///
|
||||
/// #### Negative index
|
||||
/// It's possible to specify a negative index to target the last nth element.
|
||||
/// For example, -1 targets the last element and -3 the last 3rd element.
|
||||
///
|
||||
/// #### Appending
|
||||
/// ### Appending
|
||||
/// To add a key at the end of an array, specify the `PathElement.count`
|
||||
///
|
||||
/// ### Non-existing key
|
||||
/// Any non existing key encountered in the path will be created.
|
||||
func adding(_ value: ExplorerValue, at path: Path) throws -> Self
|
||||
|
||||
// MARK: - Paths listing
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
# Custom types with ExplorerValue
|
||||
|
||||
Learn more about the back bone of the serializable ``PathExplorer``s and understand how you can use it to inject your own types when setting or adding values.
|
||||
|
||||
|
||||
## Meet ExplorerValue
|
||||
|
||||
`ExplorerValue` is a type implementing the ``PathExplorer`` protocol. As it implements `Codable` too, it can be used as a `PathExplorer` as long as a coder exists for the data format. Thus, JSON, Plist and YAML `PathExplorer`s can use the `ExplorerValue` type to get simple conformance to the protocol.
|
||||
|
||||
That's why ``CodablePathExplorer`` is mainly a wrapper around `ExplorerValue` to provide a generic structure implementing `PathExplorer`. ``PathExplorers/Json``, ``PathExplorers/Plist`` and ``PathExplorers/Yaml`` are simply type aliases for `CodablePathExplorer`, and differs only by the generic type ``CodableFormat``.
|
||||
|
||||
As `ExplorerValue` conforms to `Codable`, it's possible to provide a custom `Encoder` or `Decoder` rather than using the default ones coming with the ``PathExplorers`` namespace. This allows to specify date coding strategies for example, or to support new data formats in a blink of an eye with a dedicated Encoder/Decoder.
|
||||
|
||||
## ExplorerValueCreatable
|
||||
|
||||
To take things further, it's also possible to convert any type to an `ExplorerValue` with ``ExplorerValueRepresentable``. This protocol's only requirement is a function that returns an `ExplorerValue`. This way, it's possible to set or add a value of a custom type with a `PathExplorer`.
|
||||
|
||||
It's worth to note that making a type conform to `Encodable` is enough to make it `ExplorerValueRepresentable` too. A value of this type will be *encoded* to an `ExplorerValue`. Thus, using the following structure:
|
||||
|
||||
```swift
|
||||
struct Record: Codable, ExplorerValueConvertible {
|
||||
var name: String
|
||||
var score: Int
|
||||
}
|
||||
```
|
||||
|
||||
It's possible to set a `Record` value with any `PathExplorer`
|
||||
|
||||
```swift
|
||||
let record = Record(name: "Riri", score: 20)
|
||||
|
||||
// plist: CodablePathExplorer<PlistFormat>
|
||||
try plist.set("ducks", "records", 0, to: record)
|
||||
```
|
||||
|
||||
> Note: Even if primitive types conform to `Encodable`, it would be less efficient to encode them. A simpler implementation for `ExplorerValueRepresentable` is provided. The same goes for an array of a primitive type and for a dictionary where `Value` is a primitive type.
|
||||
|
||||
|
||||
## ExplorerValueCreatable
|
||||
|
||||
The counterpart of `ExplorerValueRepresentable` is ``ExplorerValueCreatable``. Types conforming to this protocol declare an initialization from an `ExplorerValue`. This allows to export the value of an `ExplorerValue` to the type.
|
||||
|
||||
> Tip: Similarly with `ExplorerValueRepresentable`, a default implementation is provided for primitive types and types conforming to `Decodable`.
|
||||
|
||||
With the `Record` structure from above,
|
||||
|
||||
```swift
|
||||
struct Record: Codable, ExplorerValueConvertible {
|
||||
var name: String
|
||||
var score: Int
|
||||
}
|
||||
```
|
||||
|
||||
it's possible to try to export a value of a `PathExplorer` as an array of `Record`s with ``PathExplorer/array(of:)``
|
||||
|
||||
```swift
|
||||
// plist: CodablePathExplorer<PlistFormat>
|
||||
let records = try plist.get("Riri", "records").array(of: Record.self)
|
||||
```
|
||||
|
||||
## ExplorerValueConvertible
|
||||
|
||||
``ExplorerValueConvertible`` is simply a type alias for both `ExplorerValueRepresentable` and `ExplorerValueCreatable` protocols.
|
|
@ -1,6 +0,0 @@
|
|||
# Diving into ExplorerValue
|
||||
|
||||
Learn more about the back bone of the serializable ``PathExplorer``s and understand how you can use it to inject your own types when setting or adding values.
|
||||
|
||||
|
||||
## Meet ExplorerValue
|
|
@ -69,7 +69,7 @@ More concisely, if you are only interested into getting Tom's height, you could
|
|||
let tomHeight = try yaml.get("Tom", "height").double
|
||||
```
|
||||
|
||||
> Note: As you might have noticed, calling `get()` can throw an error. This is the case for most `PathExplorer` functions. Whenever an element in the provided path does not exist, for instance an index out of bounds, or a missing key, a relevant error will be thrown to let you take the relevant action.
|
||||
> Note: As you might have noticed, calling `get()` can throw an error. This is the case for most `PathExplorer` functions. Whenever an element in the provided path does not exist, for instance an index out of bounds, or a missing key, a relevant error will be thrown.
|
||||
|
||||
As a last example, here's how to read Robert first hobby inside an array:
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# Paths listing
|
||||
|
||||
`PathExplorer` list path features is useful to get all paths leading to a value or a key.
|
||||
|
||||
## Overview
|
||||
|
||||
In this article, learn how to use the paths listing feature to precisely get the paths you want.
|
||||
|
||||
|
|
@ -6,12 +6,6 @@
|
|||
|
||||
## Overview
|
||||
|
||||
`ExplorerValue` is the back bone of serializable ``PathExplorer`` (JSON, Plist, YAML). It's the type that implements all the logic to conform to `PathExplorer`. Then ``SerializablePathExplorer`` simply interfaces it with the proper data format. Also, it's the type that is used to encode and decode to those formats.
|
||||
`ExplorerValue` is the back bone of serializable ``PathExplorer`` (JSON, Plist, YAML). It's the type that implements all the logic to conform to `PathExplorer`. Then ``CodablePathExplorer`` simply interfaces it with the proper data format to conform to ``SerializablePathExplorer``. Also, it's the type that is used to encode and decode to those formats.
|
||||
|
||||
But it also allows to use your own types to inject them in a `PathExplorer`.
|
||||
|
||||
## Topics
|
||||
|
||||
### <!--@START_MENU_TOKEN@-->Group<!--@END_MENU_TOKEN@-->
|
||||
|
||||
- <!--@START_MENU_TOKEN@-->``Symbol``<!--@END_MENU_TOKEN@-->
|
||||
But it also allows to use your own types to inject them in a `PathExplorer`. Read more with <doc:custom-types-explorerValue>
|
||||
|
|
|
@ -14,16 +14,19 @@ Supported formats:
|
|||
|
||||
### Essential
|
||||
- <doc:getting-started>
|
||||
- ``PathExplorer``
|
||||
- ``Path``
|
||||
|
||||
### Exploring data
|
||||
### Explore data
|
||||
|
||||
- ``PathExplorer``
|
||||
- ``PathExplorers``
|
||||
- ``ExplorerError``
|
||||
|
||||
### Manipulating paths
|
||||
### Manipulate paths
|
||||
|
||||
- <doc:mastering-paths>
|
||||
- <doc:listing-path>
|
||||
- ``Path``
|
||||
- ``PathElement``
|
||||
- ``PathElementRepresentable``
|
||||
|
@ -43,7 +46,7 @@ Supported formats:
|
|||
|
||||
### Set and add custom types
|
||||
|
||||
- <doc:explorer-value-diving>
|
||||
- <doc:custom-types-explorerValue>
|
||||
- ``ExplorerValue``
|
||||
- ``ExplorerValueCreatable``
|
||||
- ``ExplorerValueRepresentable``
|
||||
|
|
Loading…
Reference in New Issue