Files
swift-argument-parser/Sources/ArgumentParser/Validators/PositionalArgumentsValidator.swift

87 lines
3.0 KiB
Swift

//===----------------------------------------------------------------------===//
//
// 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 for positional argument arrays.
///
/// For positional arguments to be valid, there must be at most one
/// positional array argument, and it must be the last positional argument
/// in the argument list. Any other configuration leads to ambiguity in
/// parsing the arguments.
struct PositionalArgumentsValidator: ParsableArgumentsValidator {
struct Error: ParsableArgumentsValidatorError, CustomStringConvertible {
let repeatedPositionalArgument: String
let positionalArgumentFollowingRepeated: String
var description: String {
"""
Can't have a positional argument \
`\(positionalArgumentFollowingRepeated)` following an array of \
positional arguments `\(repeatedPositionalArgument)`.
"""
}
var kind: ValidatorErrorKind { .failure }
}
static func validate(
_ type: ParsableArguments.Type, parent: InputKey?
) -> ParsableArgumentsValidatorError? {
let sets: [ArgumentSet] = Mirror(reflecting: type.init())
.children
.compactMap { child in
guard
let codingKey = child.label,
let parsed = child.value as? ArgumentSetProvider
else { return nil }
let key = InputKey(name: codingKey, parent: parent)
return parsed.argumentSet(for: key)
}
guard
let repeatedPositional = sets.firstIndex(where: {
$0.firstRepeatedPositionalArgument != nil
})
else { return nil }
guard
let positionalFollowingRepeated = sets[repeatedPositional...]
.dropFirst()
.first(where: { $0.firstPositionalArgument != nil })
else { return nil }
// swift-format-ignore: NeverForceUnwrap
// We know these are non-nil because of the guard statements above.
let firstRepeatedPositionalArgument: ArgumentDefinition = sets[
repeatedPositional
].firstRepeatedPositionalArgument!
// swift-format-ignore: NeverForceUnwrap
let positionalFollowingRepeatedArgument: ArgumentDefinition =
positionalFollowingRepeated.firstPositionalArgument!
// swift-format-ignore: NeverForceUnwrap
return Error(
repeatedPositionalArgument: firstRepeatedPositionalArgument.help.keys
.first!.name,
positionalArgumentFollowingRepeated: positionalFollowingRepeatedArgument
.help.keys.first!.name)
}
}
extension ArgumentSet {
fileprivate var firstPositionalArgument: ArgumentDefinition? {
content.first(where: { $0.isPositional })
}
fileprivate var firstRepeatedPositionalArgument: ArgumentDefinition? {
content.first(where: { $0.isRepeatingPositional })
}
}