diff --git a/Sources/ArgumentParser/Documentation.docc/Extensions/Option.md b/Sources/ArgumentParser/Documentation.docc/Extensions/Option.md index f5a2c9a7..ebb1721d 100644 --- a/Sources/ArgumentParser/Documentation.docc/Extensions/Option.md +++ b/Sources/ArgumentParser/Documentation.docc/Extensions/Option.md @@ -2,15 +2,15 @@ ## Topics -### Single Options +### Single-Value Options -- ``init(name:parsing:help:completion:)-7slrf`` - ``init(name:parsing:help:completion:)-4yske`` +- ``init(name:parsing:help:completion:)-7slrf`` +- ``init(wrappedValue:name:parsing:help:completion:)-7ilku`` - ``init(name:parsing:help:completion:transform:)-2wf44`` - ``init(name:parsing:help:completion:transform:)-25g7b`` - ``init(wrappedValue:name:parsing:help:completion:transform:)-2llve`` - ``SingleValueParsingStrategy`` -- ``init(wrappedValue:name:parsing:help:completion:)-7ilku`` ### Array Options diff --git a/Sources/ArgumentParser/Parsable Properties/Argument.swift b/Sources/ArgumentParser/Parsable Properties/Argument.swift index b9ad507d..84a9d0ca 100644 --- a/Sources/ArgumentParser/Parsable Properties/Argument.swift +++ b/Sources/ArgumentParser/Parsable Properties/Argument.swift @@ -414,6 +414,7 @@ extension Argument { /// - transform: A closure that converts a string into this property's /// element type or throws an error. @preconcurrency + @_disfavoredOverload public init( help: ArgumentHelp? = nil, completion: CompletionKind? = nil, diff --git a/Sources/ArgumentParser/Parsable Properties/Option.swift b/Sources/ArgumentParser/Parsable Properties/Option.swift index 27a23f35..9dad1e42 100644 --- a/Sources/ArgumentParser/Parsable Properties/Option.swift +++ b/Sources/ArgumentParser/Parsable Properties/Option.swift @@ -31,7 +31,7 @@ /// /// mutating func run() { /// print("\(greeting) \(name)!") -/// if let age = age { +/// if let age { /// print("Congrats on making it to the ripe old age of \(age)!") /// } /// } @@ -226,11 +226,11 @@ public struct ArrayParsingStrategy: Hashable { /// through the terminator `--`. That is the more common approach. For example: /// ```swift /// struct Options: ParsableArguments { - /// @Option var name: String + /// @Option var title: String /// @Argument var remainder: [String] /// } /// ``` - /// would parse the input `--name Foo -- Bar --baz` such that the `remainder` + /// would parse the input `--title Foo -- Bar --baz` such that the `remainder` /// would hold the value `["Bar", "--baz"]`. public static var remaining: ArrayParsingStrategy { self.init(base: .allRemainingInput) @@ -241,20 +241,25 @@ extension ArrayParsingStrategy: Sendable { } // MARK: - @Option T: ExpressibleByArgument Initializers extension Option where Value: ExpressibleByArgument { - /// Creates a property with a default value provided by standard Swift default value syntax. + /// Creates a property with a default value that reads its value from a + /// labeled option. + /// + /// This initializer is used when you declare an `@Option`-attributed property + /// that has an `ExpressibleByArgument` type, providing a default value: /// - /// This method is called to initialize an `Option` with a default value such as: /// ```swift - /// @Option var foo: String = "bar" + /// @Option var title: String = "" /// ``` /// /// - Parameters: /// - wrappedValue: A default value to use for this property, provided - /// implicitly by the compiler during property wrapper initialization. - /// - name: A specification for what names are allowed for this flag. - /// - parsingStrategy: The behavior to use when looking for this option's value. + /// implicitly by the compiler during property wrapper initialization. + /// - name: A specification for what names are allowed for this option. + /// - parsingStrategy: The behavior to use when looking for this option's + /// value. /// - help: Information about how to use this option. - /// - completion: Kind of completion provided to the user for this option. + /// - completion: The type of command-line completion provided for this + /// option. public init( wrappedValue: Value, name: NameSpecification = .long, @@ -294,18 +299,22 @@ extension Option where Value: ExpressibleByArgument { completion: completion) } - /// Creates a property with no default value. + /// Creates a required property that reads its value from a labeled option. + /// + /// This initializer is used when you declare an `@Option`-attributed property + /// that has an `ExpressibleByArgument` type, but without a default value: /// - /// This method is called to initialize an `Option` without a default value such as: /// ```swift - /// @Option var foo: String + /// @Option var title: String /// ``` /// /// - Parameters: - /// - name: A specification for what names are allowed for this flag. - /// - parsingStrategy: The behavior to use when looking for this option's value. + /// - name: A specification for what names are allowed for this option. + /// - parsingStrategy: The behavior to use when looking for this option's + /// value. /// - help: Information about how to use this option. - /// - completion: Kind of completion provided to the user for this option. + /// - completion: The type of command-line completion provided for this + /// option. public init( name: NameSpecification = .long, parsing parsingStrategy: SingleValueParsingStrategy = .next, @@ -329,22 +338,28 @@ extension Option where Value: ExpressibleByArgument { // MARK: - @Option T Initializers extension Option { - /// Creates a property with a default value provided by standard Swift default value syntax. + /// Creates a property with a default value that reads its value from a + /// labeled option, parsing with the given closure. + /// + /// This initializer is used when you declare an `@Option`-attributed property + /// with a transform closure and a default value: /// - /// This method is called to initialize an `Option` with a default value such as: /// ```swift - /// @Option var foo: String = "bar" + /// @Option(transform: { $0.first ?? " " }) + /// var char: Character = "_" /// ``` /// /// - Parameters: - /// - wrappedValue: A default value to use for this property, provided - /// implicitly by the compiler during property wrapper initialization. - /// - name: A specification for what names are allowed for this flag. - /// - parsingStrategy: The behavior to use when looking for this option's value. + /// - wrappedValue: The default value to use for this property, provided + /// implicitly by the compiler during property wrapper initialization. + /// - name: A specification for what names are allowed for this option. + /// - parsingStrategy: The behavior to use when looking for this option's + /// value. /// - help: Information about how to use this option. - /// - completion: Kind of completion provided to the user for this option. + /// - completion: The type of command-line completion provided for this + /// option. /// - transform: A closure that converts a string into this property's - /// element type or throws an error. + /// type, or else throws an error. @preconcurrency public init( wrappedValue: Value, @@ -369,19 +384,28 @@ extension Option { }) } - /// Creates a property with no default value. + /// Creates a required property that reads its value from a labeled option, + /// parsing with the given closure. + /// + /// This initializer is used when you declare an `@Option`-attributed property + /// with a transform closure and without a default value: /// - /// This method is called to initialize an `Option` without a default value such as: /// ```swift - /// @Option var foo: String + /// @Option(transform: { $0.first ?? " " }) + /// var char: Character /// ``` /// /// - Parameters: - /// - name: A specification for what names are allowed for this flag. - /// - parsingStrategy: The behavior to use when looking for this option's value. + /// - name: A specification for what names are allowed for this option. + /// - parsingStrategy: The behavior to use when looking for this option's + /// value. /// - help: Information about how to use this option. - /// - completion: Kind of completion provided to the user for this option. + /// - completion: The type of command-line completion provided for this + /// option. + /// - transform: A closure that converts a string into this property's + /// type, or else throws an error. @preconcurrency + @_disfavoredOverload public init( name: NameSpecification = .long, parsing parsingStrategy: SingleValueParsingStrategy = .next, @@ -407,16 +431,23 @@ extension Option { // MARK: - @Option Optional<T: ExpressibleByArgument> Initializers extension Option { + /// Creates an optional property that reads its value from a labeled option, + /// with an explicit `nil` default. + /// /// This initializer allows a user to provide a `nil` default value for an - /// optional `@Option`-marked property without allowing a non-`nil` default - /// value. + /// optional `@Option`-marked property: + /// + /// ```swift + /// @Option var count: Int? = nil + /// ``` /// /// - Parameters: - /// - name: A specification for what names are allowed for this flag. + /// - name: A specification for what names are allowed for this option. /// - parsingStrategy: The behavior to use when looking for this option's /// value. /// - help: Information about how to use this option. - /// - completion: Kind of completion provided to the user for this option. + /// - completion: The type of command-line completion provided for this + /// option. public init<T>( wrappedValue _value: _OptionalNilComparisonType, name: NameSpecification = .long, @@ -463,18 +494,22 @@ extension Option { }) } - /// Creates a property that reads its value from a labeled option. + /// Creates an optional property that reads its value from a labeled option. /// - /// If the property has an `Optional` type, or you provide a non-`nil` - /// value for the `initial` parameter, specifying this option is not - /// required. + /// This initializer is used when you declare an `@Option`-attributed property + /// with an optional type and no default value: + /// + /// ```swift + /// @Option var count: Int? + /// ``` /// /// - Parameters: - /// - name: A specification for what names are allowed for this flag. + /// - name: A specification for what names are allowed for this option. /// - parsingStrategy: The behavior to use when looking for this option's /// value. /// - help: Information about how to use this option. - /// - completion: Kind of completion provided to the user for this option. + /// - completion: The type of command-line completion provided for this + /// option. public init<T>( name: NameSpecification = .long, parsing parsingStrategy: SingleValueParsingStrategy = .next, @@ -498,24 +533,28 @@ extension Option { // MARK: - @Option Optional<T> Initializers extension Option { - /// Creates a property with a default value provided by standard Swift default - /// value syntax, parsing with the given closure. + /// Creates an optional property that reads its value from a labeled option, + /// parsing with the given closure, with an explicit `nil` default. + /// + /// This initializer is used when you declare an `@Option`-attributed property + /// with a transform closure and with a default value of `nil`: /// - /// This method is called to initialize an `Option` with a default value such as: /// ```swift - /// @Option(transform: baz) - /// var foo: String = "bar" + /// @Option(transform: { $0.first ?? " " }) + /// var char: Character? = nil /// ``` /// /// - Parameters: /// - wrappedValue: A default value to use for this property, provided - /// implicitly by the compiler during property wrapper initialization. - /// - name: A specification for what names are allowed for this flag. - /// - parsingStrategy: The behavior to use when looking for this option's value. + /// implicitly by the compiler during property wrapper initialization. + /// - name: A specification for what names are allowed for this option. + /// - parsingStrategy: The behavior to use when looking for this option's + /// value. /// - help: Information about how to use this option. - /// - completion: Kind of completion provided to the user for this option. - /// - transform: A closure that converts a string into this property's type - /// or throws an error. + /// - completion: The type of command-line completion provided for this + /// option. + /// - transform: A closure that converts a string into this property's + /// type, or else throws an error. @preconcurrency public init<T>( wrappedValue _value: _OptionalNilComparisonType, @@ -568,20 +607,26 @@ extension Option { }) } - /// Creates a property with no default value, parsing with the given closure. + /// Creates an optional property that reads its value from a labeled option, + /// parsing with the given closure. + /// + /// This initializer is used when you declare an `@Option`-attributed property + /// with a transform closure and without a default value: /// - /// This method is called to initialize an `Option` with no default value such as: /// ```swift - /// @Option(transform: baz) - /// var foo: String + /// @Option(transform: { $0.first ?? " " }) + /// var char: Character? /// ``` /// /// - Parameters: - /// - name: A specification for what names are allowed for this flag. - /// - parsingStrategy: The behavior to use when looking for this option's value. + /// - name: A specification for what names are allowed for this option. + /// - parsingStrategy: The behavior to use when looking for this option's + /// value. /// - help: Information about how to use this option. - /// - completion: Kind of completion provided to the user for this option. - /// - transform: A closure that converts a string into this property's type or throws an error. + /// - completion: The type of command-line completion provided for this + /// option. + /// - transform: A closure that converts a string into this property's + /// type, or else throws an error. @preconcurrency public init<T>( name: NameSpecification = .long, @@ -608,16 +653,30 @@ extension Option { // MARK: - @Option Array<T: ExpressibleByArgument> Initializers extension Option { - /// Creates an array property that reads its values from zero or more - /// labeled options. + /// Creates an array property that reads its values from zero or + /// more labeled options. + /// + /// This initializer is used when you declare an `@Option`-attributed array + /// property with a default value: + /// + /// ```swift + /// @Option(name: .customLong("char")) + /// var chars: [Character] = [] + /// ``` /// /// - Parameters: - /// - name: A specification for what names are allowed for this flag. - /// - initial: A default value to use for this property. - /// - parsingStrategy: The behavior to use when parsing multiple values - /// from the command-line arguments. + /// - wrappedValue: A default value to use for this property, provided + /// implicitly by the compiler during property wrapper initialization. + /// If this initial value is non-empty, elements passed from the command + /// line are appended to the original contents. + /// - name: A specification for what names are allowed for this option. + /// - parsingStrategy: The behavior to use when parsing the elements for + /// this option. /// - help: Information about how to use this option. - /// - completion: Kind of completion provided to the user for this option. + /// - completion: The type of command-line completion provided for this + /// option. + /// - transform: A closure that converts a string into this property's + /// element type, or else throws an error. public init<T>( wrappedValue: Array<T>, name: NameSpecification = .long, @@ -639,19 +698,24 @@ extension Option { }) } - /// Creates an array property with no default value that reads its values from zero or more labeled options. + /// Creates a required array property that reads its values from zero or + /// more labeled options. + /// + /// This initializer is used when you declare an `@Option`-attributed array + /// property without a default value: /// - /// This method is called to initialize an array `Option` with no default value such as: /// ```swift - /// @Option() - /// var foo: [String] + /// @Option(name: .customLong("char")) + /// var chars: [Character] /// ``` /// /// - Parameters: - /// - name: A specification for what names are allowed for this flag. - /// - parsingStrategy: The behavior to use when parsing multiple values from the command-line arguments. + /// - name: A specification for what names are allowed for this option. + /// - parsingStrategy: The behavior to use when parsing the elements for + /// this option. /// - help: Information about how to use this option. - /// - completion: Kind of completion provided to the user for this option. + /// - completion: The type of command-line completion provided for this + /// option. public init<T>( name: NameSpecification = .long, parsing parsingStrategy: ArrayParsingStrategy = .singleValue, @@ -675,22 +739,30 @@ extension Option { // MARK: - @Option Array<T> Initializers extension Option { - /// Creates an array property that reads its values from zero or more - /// labeled options, parsing with the given closure. + /// Creates an array property that reads its values from zero or + /// more labeled options, parsing each element with the given closure. /// - /// This property defaults to an empty array if the `initial` parameter - /// is not specified. + /// This initializer is used when you declare an `@Option`-attributed array + /// property with a transform closure and a default value: + /// + /// ```swift + /// @Option(name: .customLong("char"), transform: { $0.first ?? " " }) + /// var chars: [Character] = [] + /// ``` /// /// - Parameters: - /// - name: A specification for what names are allowed for this flag. - /// - initial: A default value to use for this property. If `initial` is - /// `nil`, this option defaults to an empty array. - /// - parsingStrategy: The behavior to use when parsing multiple values - /// from the command-line arguments. + /// - wrappedValue: A default value to use for this property, provided + /// implicitly by the compiler during property wrapper initialization. + /// If this initial value is non-empty, elements passed from the command + /// line are appended to the original contents. + /// - name: A specification for what names are allowed for this option. + /// - parsingStrategy: The behavior to use when parsing the elements for + /// this option. /// - help: Information about how to use this option. - /// - completion: Kind of completion provided to the user for this option. + /// - completion: The type of command-line completion provided for this + /// option. /// - transform: A closure that converts a string into this property's - /// element type or throws an error. + /// element type, or else throws an error. @preconcurrency public init<T>( wrappedValue: Array<T>, @@ -715,23 +787,26 @@ extension Option { }) } - /// Creates an array property with no default value that reads its values from - /// zero or more labeled options, parsing each element with the given closure. + /// Creates a required array property that reads its values from zero or + /// more labeled options, parsing each element with the given closure. + /// + /// This initializer is used when you declare an `@Option`-attributed array + /// property with a transform closure and without a default value: /// - /// This method is called to initialize an array `Option` with no default value such as: /// ```swift - /// @Option(transform: baz) - /// var foo: [String] + /// @Option(name: .customLong("char"), transform: { $0.first ?? " " }) + /// var chars: [Character] /// ``` /// /// - Parameters: - /// - name: A specification for what names are allowed for this flag. - /// - parsingStrategy: The behavior to use when parsing multiple values from - /// the command-line arguments. + /// - name: A specification for what names are allowed for this option. + /// - parsingStrategy: The behavior to use when parsing the elements for + /// this option. /// - help: Information about how to use this option. - /// - completion: Kind of completion provided to the user for this option. + /// - completion: The type of command-line completion provided for this + /// option. /// - transform: A closure that converts a string into this property's - /// element type or throws an error. + /// element type, or else throws an error. @preconcurrency public init<T>( name: NameSpecification = .long, diff --git a/Tests/ArgumentParserEndToEndTests/OptionalEndToEndTests.swift b/Tests/ArgumentParserEndToEndTests/OptionalEndToEndTests.swift index cbb3b830..9d941554 100644 --- a/Tests/ArgumentParserEndToEndTests/OptionalEndToEndTests.swift +++ b/Tests/ArgumentParserEndToEndTests/OptionalEndToEndTests.swift @@ -206,3 +206,29 @@ extension OptionalEndToEndTests { XCTAssertThrowsError(try Bar.parse(["-f", "--name", "A"])) } } + +extension OptionalEndToEndTests { + // Compilation test: https://github.com/apple/swift-argument-parser/issues/618 + private struct Command: ParsableCommand { + struct MyError: Error {} + struct Foo { + init?(string: String) { return nil } + } + + @Option(transform: { + guard let foo = Foo(string: $0) else { + throw MyError() + } + return foo + }) + var testOption: Foo? + + @Argument(transform: { + guard let foo = Foo(string: $0) else { + throw MyError() + } + return foo + }) + var testArgument: Foo? + } +}