121 lines
5.0 KiB
Swift
121 lines
5.0 KiB
Swift
//
|
|
// Scout
|
|
// Copyright (c) 2020-present Alexis Bridoux
|
|
// MIT license, see LICENSE file for details
|
|
|
|
import Foundation
|
|
|
|
/// Errors that can be thrown when exploring data using a ``PathExplorer``
|
|
public struct ExplorerError: LocalizedError, Equatable {
|
|
public private(set) var path: Path
|
|
let description: String
|
|
|
|
public var errorDescription: String? { "'\(path.description)' ▶︎ \(description)" }
|
|
|
|
init(path: Path = .empty, description: String) {
|
|
self.path = path
|
|
self.description = description
|
|
}
|
|
}
|
|
|
|
// MARK: - Path building
|
|
|
|
extension ExplorerError {
|
|
|
|
/// Return a new `ValueTypeError` with the provided path, only when the current path is not empty.
|
|
/// If the current path is not empty, self is returned
|
|
func with(path: Path) -> Self {
|
|
guard self.path.isEmpty else { return self }
|
|
return ExplorerError(path: path, description: description)
|
|
}
|
|
|
|
/// Return a new `ValueTypeError` with the provided path, only when the current path is not empty.
|
|
/// If the current path is not empty, self is returned
|
|
func with(path: PathElement...) -> Self {
|
|
guard self.path.isEmpty else { return self }
|
|
return ExplorerError(path: Path(path), description: description)
|
|
}
|
|
|
|
/// Return a new `ValueTypeError` with the provided path, only when the current path is not empty.
|
|
/// If the current path is not empty, self is returned
|
|
func with(path: [PathElement]) -> Self {
|
|
guard self.path.isEmpty else { return self }
|
|
return ExplorerError(path: Path(path), description: description)
|
|
}
|
|
|
|
/// Return a new `ValueTypeError` with the provided path, only when the current path is not empty.
|
|
/// If the current path is not empty, self is returned
|
|
func with(path: Slice<Path>) -> Self {
|
|
guard self.path.isEmpty else { return self }
|
|
return ExplorerError(path: Path(path), description: description)
|
|
}
|
|
|
|
func adding(_ element: PathElementRepresentable) -> ExplorerError {
|
|
ExplorerError(path: path.appending(element), description: description)
|
|
}
|
|
}
|
|
|
|
// MARK: - Registered errors
|
|
|
|
public extension ExplorerError {
|
|
|
|
/// The provided value is not convertible to an ``ExplorerValue``
|
|
static func invalid(value: Any) -> Self {
|
|
ExplorerError(description: "The value \(value) is not convertible to ExplorerValue")
|
|
}
|
|
|
|
/// The key used to subscript is missing. A best match in the existing key is provided if one is found.
|
|
static func missing(key: String, bestMatch: String?) -> Self {
|
|
ExplorerError(description: "Missing key '\(key)' in dictionary. Best match found: '\(bestMatch ?? "none")'")
|
|
}
|
|
|
|
/// Trying to subscript something with a key although it's not a dictionary
|
|
static var subscriptKeyNoDict: Self {
|
|
ExplorerError(description: "The value cannot be subscripted with a string as it is not a dictionary")
|
|
}
|
|
|
|
/// The provided index is out of bounds to subscript the array
|
|
static func wrong(index: Int, arrayCount: Int) -> Self {
|
|
ExplorerError(description: "Index \(index) out of bounds to subscript the array with \(arrayCount) elements")
|
|
}
|
|
|
|
/// Trying to subscript something with an index although it's not an array
|
|
static var subscriptIndexNoArray: Self {
|
|
ExplorerError(description: "The value cannot be subscripted with an index as it is not an array")
|
|
}
|
|
|
|
/// The ``PathElement`` is misplaced or forbidden for the feature
|
|
static func wrongUsage(of element: PathElement) -> Self {
|
|
return ExplorerError(description: "The element \(element.kindDescription) \(element) cannot be used here. \(element.usage)")
|
|
}
|
|
|
|
/// The bounds provided to the ``PathElement/slice(_:)`` element are not valid to slice the array.
|
|
static func wrong(bounds: Bounds, arrayCount: Int) -> Self {
|
|
let description =
|
|
"""
|
|
Wrong slice '[\(bounds.lowerString):\(bounds.upperString)]' for array with count: \(arrayCount).
|
|
Valid slice: 0 <= lowerBound <= upperBound < arrayCount. Negative bounds are subtracted from the array count (-bound -> arrayCount - bound).
|
|
Omit lower to target first index. Omit upper to target last index.
|
|
"""
|
|
|
|
return ExplorerError(description: description)
|
|
}
|
|
|
|
/// The regular expression pattern is invalid
|
|
static func wrong(regexPattern: String) -> Self {
|
|
ExplorerError(description: "The string '\(regexPattern)' is not a valid regular expression pattern")
|
|
}
|
|
|
|
/// The conversion from an ``ExplorerValue`` to the provided type has failed
|
|
static func mismatchingType<T>(_ type: T.Type, value: ExplorerValue) -> Self {
|
|
ExplorerError(description: "ExplorerValue '\(value)' cannot be represented as \(T.self)")
|
|
}
|
|
|
|
/// The predicate in invalid.
|
|
///
|
|
/// For instance, a `String` value is evaluated against a predicate taking an `Int` as input
|
|
static func predicateNotEvaluatable(_ predicate: String, description: String) -> Self {
|
|
ExplorerError(description: #"Unable to evaluate the predicate "\#(predicate)". \#(description)"#)
|
|
}
|
|
}
|