diff --git a/Examples/repeat/main.swift b/Examples/repeat/main.swift index da74e6851..55a9eec47 100644 --- a/Examples/repeat/main.swift +++ b/Examples/repeat/main.swift @@ -19,11 +19,15 @@ struct Repeat: ParsableCommand { var includeCounter: Bool @Argument(help: "The phrase to repeat.") - var phrase: String + var phrase: [String] func run() throws { let repeatCount = count ?? .max + dump($count) + dump($includeCounter) + dump($phrase) + for i in 1...repeatCount { if includeCounter { print("\(i): \(phrase)") diff --git a/Sources/ArgumentParser/Parsable Properties/Argument.swift b/Sources/ArgumentParser/Parsable Properties/Argument.swift index 529ba3dc0..fe5685415 100644 --- a/Sources/ArgumentParser/Parsable Properties/Argument.swift +++ b/Sources/ArgumentParser/Parsable Properties/Argument.swift @@ -40,14 +40,23 @@ public struct Argument: public var wrappedValue: Value { get { switch _parsedValue { - case .value(let v): + case .value(let v, _): return v case .definition: fatalError(directlyInitializedError) } } set { - _parsedValue = .value(newValue) + _parsedValue = .value(newValue, _parsedValue.source ?? ArgumentSource(source: [])) + } + } + + public var projectedValue: ArgumentSource { + switch _parsedValue { + case .value(_, let source): + return source + case .definition: + fatalError(directlyInitializedError) } } } @@ -55,7 +64,7 @@ public struct Argument: extension Argument: CustomStringConvertible { public var description: String { switch _parsedValue { - case .value(let v): + case .value(let v, _): return String(describing: v) case .definition: return "Argument(*definition*)" diff --git a/Sources/ArgumentParser/Parsable Properties/ArgumentSource.swift b/Sources/ArgumentParser/Parsable Properties/ArgumentSource.swift new file mode 100644 index 000000000..dde15cb19 --- /dev/null +++ b/Sources/ArgumentParser/Parsable Properties/ArgumentSource.swift @@ -0,0 +1,15 @@ +//===----------------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +public struct ArgumentSource { + public var source: [(value: String, position: Int)] +} + diff --git a/Sources/ArgumentParser/Parsable Properties/Flag.swift b/Sources/ArgumentParser/Parsable Properties/Flag.swift index 0b1ef93cc..dbf218dc9 100644 --- a/Sources/ArgumentParser/Parsable Properties/Flag.swift +++ b/Sources/ArgumentParser/Parsable Properties/Flag.swift @@ -52,14 +52,23 @@ public struct Flag: Decodable, ParsedWrapper { public var wrappedValue: Value { get { switch _parsedValue { - case .value(let v): + case .value(let v, _): return v case .definition: fatalError(directlyInitializedError) } } set { - _parsedValue = .value(newValue) + _parsedValue = .value(newValue, _parsedValue.source ?? ArgumentSource(source: [])) + } + } + + public var projectedValue: ArgumentSource { + switch _parsedValue { + case .value(_, let source): + return source + case .definition: + fatalError(directlyInitializedError) } } } @@ -67,7 +76,7 @@ public struct Flag: Decodable, ParsedWrapper { extension Flag: CustomStringConvertible { public var description: String { switch _parsedValue { - case .value(let v): + case .value(let v, _): return String(describing: v) case .definition: return "Flag(*definition*)" diff --git a/Sources/ArgumentParser/Parsable Properties/Option.swift b/Sources/ArgumentParser/Parsable Properties/Option.swift index 1e559f3af..e39686400 100644 --- a/Sources/ArgumentParser/Parsable Properties/Option.swift +++ b/Sources/ArgumentParser/Parsable Properties/Option.swift @@ -42,14 +42,23 @@ public struct Option: Decodable, ParsedWrapper { public var wrappedValue: Value { get { switch _parsedValue { - case .value(let v): + case .value(let v, _): return v case .definition: fatalError(directlyInitializedError) } } set { - _parsedValue = .value(newValue) + _parsedValue = .value(newValue, _parsedValue.source ?? ArgumentSource(source: [])) + } + } + + public var projectedValue: ArgumentSource { + switch _parsedValue { + case .value(_, let source): + return source + case .definition: + fatalError(directlyInitializedError) } } } @@ -57,7 +66,7 @@ public struct Option: Decodable, ParsedWrapper { extension Option: CustomStringConvertible { public var description: String { switch _parsedValue { - case .value(let v): + case .value(let v, _): return String(describing: v) case .definition: return "Option(*definition*)" diff --git a/Sources/ArgumentParser/Parsable Properties/OptionGroup.swift b/Sources/ArgumentParser/Parsable Properties/OptionGroup.swift index 147e3d3f7..fcb22276f 100644 --- a/Sources/ArgumentParser/Parsable Properties/OptionGroup.swift +++ b/Sources/ArgumentParser/Parsable Properties/OptionGroup.swift @@ -44,7 +44,7 @@ public struct OptionGroup: Decodable, ParsedWrapper { if let d = decoder as? SingleValueDecoder, let value = try? d.previousValue(Value.self) { - self.init(_parsedValue: .value(value)) + self.init(_parsedValue: .value(value, ArgumentSource(source: []))) } else { try self.init(_decoder: decoder) if let d = decoder as? SingleValueDecoder { @@ -63,14 +63,14 @@ public struct OptionGroup: Decodable, ParsedWrapper { public var wrappedValue: Value { get { switch _parsedValue { - case .value(let v): + case .value(let v, _): return v case .definition: fatalError(directlyInitializedError) } } set { - _parsedValue = .value(newValue) + _parsedValue = .value(newValue, _parsedValue.source ?? ArgumentSource(source: [])) } } } @@ -78,7 +78,7 @@ public struct OptionGroup: Decodable, ParsedWrapper { extension OptionGroup: CustomStringConvertible { public var description: String { switch _parsedValue { - case .value(let v): + case .value(let v, _): return String(describing: v) case .definition: return "OptionGroup(*definition*)" diff --git a/Sources/ArgumentParser/Parsing/Parsed.swift b/Sources/ArgumentParser/Parsing/Parsed.swift index 7f045834c..264eddd8a 100644 --- a/Sources/ArgumentParser/Parsing/Parsed.swift +++ b/Sources/ArgumentParser/Parsing/Parsed.swift @@ -18,12 +18,26 @@ enum Parsed { var makeSet: (InputKey) -> ArgumentSet } - case value(Value) + case value(Value, ArgumentSource) case definition(Definition) internal init(_ makeSet: @escaping (InputKey) -> ArgumentSet) { self = .definition(Definition(makeSet: makeSet)) } + + var value: Value? { + switch self { + case .value(let v, _): return v + case .definition: return nil + } + } + + var source: ArgumentSource? { + switch self { + case .value(_, let s): return s + case .definition: return nil + } + } } /// A type that wraps a `Parsed` instance to act as a property wrapper. @@ -54,7 +68,14 @@ extension ParsedWrapper { throw ParserError.noValue(forKey: d.parsedElement?.key ?? d.key) } - self.init(_parsedValue: .value(value)) + let sourceIndexes: [Int] = d.parsedElement!.inputOrigin.elements.compactMap { x -> Int? in + guard case .argumentIndex(let i) = x else { return nil } + return i.inputIndex.rawValue + } + let sourceValues = sourceIndexes.map { d.underlying.values.originalInput[$0] } + let source = ArgumentSource(source: Array(zip(sourceValues, sourceIndexes))) + + self.init(_parsedValue: .value(value, source)) } func argumentSet(for key: InputKey) -> ArgumentSet { @@ -70,18 +91,25 @@ extension ParsedWrapper { extension ParsedWrapper where Value: Decodable { init(_decoder: Decoder) throws { var value: Value + let source: ArgumentSource do { value = try Value.init(from: _decoder) + source = ArgumentSource(source: []) } catch { - if let d = _decoder as? SingleValueDecoder, - let v = d.parsedElement?.value as? Value { - value = v - } else { + guard let d = _decoder as? SingleValueDecoder, + let v = d.parsedElement?.value as? Value else { throw error } - } + value = v - self.init(_parsedValue: .value(value)) + let sourceIndexes: [Int] = d.parsedElement!.inputOrigin.elements.compactMap { x -> Int? in + guard case .argumentIndex(let i) = x else { return nil } + return i.inputIndex.rawValue + } + let sourceValues = sourceIndexes.map { d.underlying.values.originalInput[$0] } + source = ArgumentSource(source: Array(zip(sourceValues, sourceIndexes))) + } + self.init(_parsedValue: .value(value, source)) } } diff --git a/Sources/ArgumentParser/Usage/HelpCommand.swift b/Sources/ArgumentParser/Usage/HelpCommand.swift index 8afed5cfb..7de19d84c 100644 --- a/Sources/ArgumentParser/Usage/HelpCommand.swift +++ b/Sources/ArgumentParser/Usage/HelpCommand.swift @@ -36,11 +36,17 @@ struct HelpCommand: ParsableCommand { init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - self._subcommands = Argument(_parsedValue: .value(try container.decode([String].self, forKey: .subcommands))) + self._subcommands = Argument( + _parsedValue: .value( + try container.decode([String].self, forKey: .subcommands), + ArgumentSource(source: []))) } init(commandStack: [ParsableCommand.Type]) { self.commandStack = commandStack - self._subcommands = Argument(_parsedValue: .value(commandStack.map { $0._commandName })) + self._subcommands = Argument( + _parsedValue: .value( + commandStack.map { $0._commandName }, + ArgumentSource(source: []))) } }