| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- //===----------------------------------------------------------------------===//
- //
- // This source file is part of the Swift Argument Parser open source project
- //
- // Copyright (c) 2020 Apple Inc. and the Swift project authors
- // Licensed under Apache License v2.0 with Runtime Library Exception
- //
- // See https://swift.org/LICENSE.txt for license information
- //
- //===----------------------------------------------------------------------===//
- /// A validator that ensures that all arguments have corresponding coding keys.
- struct CodingKeyValidator: ParsableArgumentsValidator {
- private struct Validator: Decoder {
- let argumentKeys: [InputKey]
- enum ValidationResult: Swift.Error {
- case success
- case missingCodingKeys([InputKey])
- }
- let codingPath: [CodingKey] = []
- let userInfo: [CodingUserInfoKey: Any] = [:]
- func unkeyedContainer() throws -> UnkeyedDecodingContainer {
- fatalError()
- }
- func singleValueContainer() throws -> SingleValueDecodingContainer {
- fatalError()
- }
- func container<Key>(keyedBy type: Key.Type) throws
- -> KeyedDecodingContainer<Key> where Key: CodingKey
- {
- let missingKeys = argumentKeys.filter { Key(stringValue: $0.name) == nil }
- if missingKeys.isEmpty {
- throw ValidationResult.success
- } else {
- throw ValidationResult.missingCodingKeys(missingKeys)
- }
- }
- }
- /// This error indicates that an option, a flag, or an argument of
- /// a `ParsableArguments` is defined without a corresponding `CodingKey`.
- struct MissingKeysError: ParsableArgumentsValidatorError,
- CustomStringConvertible
- {
- let missingCodingKeys: [InputKey]
- var description: String {
- let resolution = """
- To resolve this error, make sure that all properties have \
- corresponding cases in your custom `CodingKey` enumeration.
- """
- if missingCodingKeys.count > 1 {
- return """
- Arguments \(missingCodingKeys.map({ "`\($0)`" }).joined(separator: ",")) \
- are defined without corresponding `CodingKey`s.
- \(resolution)
- """
- } else {
- return """
- Argument `\(missingCodingKeys[0])` is defined without a \
- corresponding `CodingKey`.
- \(resolution)
- """
- }
- }
- var kind: ValidatorErrorKind {
- .failure
- }
- }
- struct InvalidDecoderError: ParsableArgumentsValidatorError,
- CustomStringConvertible
- {
- let type: ParsableArguments.Type
- var description: String {
- """
- The implementation of `init(from:)` for `\(type)` \
- is not compatible with ArgumentParser. To resolve this issue, make sure \
- that `init(from:)` calls the `container(keyedBy:)` method on the given \
- decoder and decodes each of its properties using the returned decoder.
- """
- }
- var kind: ValidatorErrorKind {
- .failure
- }
- }
- static func validate(_ type: ParsableArguments.Type, parent: InputKey?)
- -> ParsableArgumentsValidatorError?
- {
- let argumentKeys: [InputKey] = Mirror(reflecting: type.init())
- .children
- .compactMap { child in
- guard
- let codingKey = child.label,
- child.value as? ArgumentSetProvider != nil
- else { return nil }
- // Property wrappers have underscore-prefixed names
- return InputKey(name: codingKey, parent: parent)
- }
- guard argumentKeys.count > 0 else {
- return nil
- }
- do {
- let _ = try type.init(from: Validator(argumentKeys: argumentKeys))
- return InvalidDecoderError(type: type)
- } catch let result as Validator.ValidationResult {
- switch result {
- case .missingCodingKeys(let keys):
- return MissingKeysError(missingCodingKeys: keys)
- case .success:
- return nil
- }
- } catch {
- fatalError("Unexpected validation error: \(error)")
- }
- }
- }
|