PositionalArgumentsValidator.swift 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. //===----------------------------------------------------------------------===//
  2. //
  3. // This source file is part of the Swift Argument Parser open source project
  4. //
  5. // Copyright (c) 2020 Apple Inc. and the Swift project authors
  6. // Licensed under Apache License v2.0 with Runtime Library Exception
  7. //
  8. // See https://swift.org/LICENSE.txt for license information
  9. //
  10. //===----------------------------------------------------------------------===//
  11. /// A validator for positional argument arrays.
  12. ///
  13. /// For positional arguments to be valid, there must be at most one
  14. /// positional array argument, and it must be the last positional argument
  15. /// in the argument list. Any other configuration leads to ambiguity in
  16. /// parsing the arguments.
  17. struct PositionalArgumentsValidator: ParsableArgumentsValidator {
  18. struct Error: ParsableArgumentsValidatorError, CustomStringConvertible {
  19. let repeatedPositionalArgument: String
  20. let positionalArgumentFollowingRepeated: String
  21. var description: String {
  22. """
  23. Can't have a positional argument \
  24. `\(positionalArgumentFollowingRepeated)` following an array of \
  25. positional arguments `\(repeatedPositionalArgument)`.
  26. """
  27. }
  28. var kind: ValidatorErrorKind { .failure }
  29. }
  30. static func validate(
  31. _ type: ParsableArguments.Type, parent: InputKey?
  32. ) -> ParsableArgumentsValidatorError? {
  33. let sets: [ArgumentSet] = Mirror(reflecting: type.init())
  34. .children
  35. .compactMap { child in
  36. guard
  37. let codingKey = child.label,
  38. let parsed = child.value as? ArgumentSetProvider
  39. else { return nil }
  40. let key = InputKey(name: codingKey, parent: parent)
  41. return parsed.argumentSet(for: key)
  42. }
  43. guard
  44. let repeatedPositional = sets.firstIndex(where: {
  45. $0.firstRepeatedPositionalArgument != nil
  46. })
  47. else { return nil }
  48. guard
  49. let positionalFollowingRepeated = sets[repeatedPositional...]
  50. .dropFirst()
  51. .first(where: { $0.firstPositionalArgument != nil })
  52. else { return nil }
  53. // swift-format-ignore: NeverForceUnwrap
  54. // We know these are non-nil because of the guard statements above.
  55. let firstRepeatedPositionalArgument: ArgumentDefinition = sets[
  56. repeatedPositional
  57. ].firstRepeatedPositionalArgument!
  58. // swift-format-ignore: NeverForceUnwrap
  59. let positionalFollowingRepeatedArgument: ArgumentDefinition =
  60. positionalFollowingRepeated.firstPositionalArgument!
  61. // swift-format-ignore: NeverForceUnwrap
  62. return Error(
  63. repeatedPositionalArgument: firstRepeatedPositionalArgument.help.keys
  64. .first!.name,
  65. positionalArgumentFollowingRepeated: positionalFollowingRepeatedArgument
  66. .help.keys.first!.name)
  67. }
  68. }
  69. extension ArgumentSet {
  70. fileprivate var firstPositionalArgument: ArgumentDefinition? {
  71. content.first(where: { $0.isPositional })
  72. }
  73. fileprivate var firstRepeatedPositionalArgument: ArgumentDefinition? {
  74. content.first(where: { $0.isRepeatingPositional })
  75. }
  76. }