2020/// their default value. Options that are neither declared as `Optional` nor
2121/// given a default value are required for users of your command-line tool.
2222///
23- /// For example, the following program defines three options:
23+ /// ## Specifying Default Values
24+ ///
25+ /// There are multiple ways to specify default values for options:
26+ ///
27+ /// 1. **Property initialization**: Assign a default value when declaring the property.
28+ /// 2. **Parsing strategy with default**: Use `.scanningForValue(default:)` to specify
29+ /// a default value that will be used when the option is not provided.
30+ ///
31+ /// For example, the following program defines options with different default approaches:
2432///
2533/// ```swift
2634/// @main
2735/// struct Greet: ParsableCommand {
28- /// @Option var greeting = "Hello"
36+ /// @Option var greeting = "Hello" // Property initialization
2937/// @Option var age: Int? = nil
3038/// @Option var name: String
39+ /// @Option(parsing: .scanningForValue(default: "Guest"))
40+ /// var fallbackName: String? // Parsing strategy default
3141///
3242/// mutating func run() {
33- /// print("\(greeting) \(name)!")
43+ /// let actualName = name.isEmpty ? fallbackName : name
44+ /// print("\(greeting) \(actualName)!")
3445/// if let age {
3546/// print("Congrats on making it to the ripe old age of \(age)!")
3647/// }
4152/// `greeting` has a default value of `"Hello"`, which can be overridden by
4253/// providing a different string as an argument, while `age` defaults to `nil`.
4354/// `name` is a required option because it is non-`nil` and has no default
44- /// value.
55+ /// value. `fallbackName` uses a parsing strategy default and will be set to
56+ /// `"Guest"` when the option is not provided.
4557///
4658/// $ greet --name Alicia
4759/// Hello Alicia!
4860/// $ greet --age 28 --name Seungchin --greeting Hi
4961/// Hi Seungchin!
5062/// Congrats on making it to the ripe old age of 28!
63+ /// $ greet --fallback-name Bob --name ""
64+ /// Hello Bob!
5165@propertyWrapper
5266public struct Option < Value> : Decodable , ParsedWrapper {
5367 internal var _parsedValue : Parsed < Value >
5468
69+ /// Error message for conflicting default values between property initialization and parsing strategy.
70+ private static var conflictingDefaultsErrorMessage : String {
71+ [
72+ " Cannot specify both a wrapped value (property default) and a parsing strategy default with .scanningForValue(default:). " ,
73+ " Use either property initialization (var name: String = \" defaultValue \" ) or parsing strategy default " ,
74+ " (@Option(parsing: .scanningForValue(default: \" defaultValue \" )) var name: String), but not both. " ,
75+ ] . joined ( separator: " " )
76+ }
77+
5578 internal init ( _parsedValue: Parsed < Value > ) {
5679 self . _parsedValue = _parsedValue
5780 }
@@ -159,6 +182,18 @@ public struct SingleValueParsingStrategy: Hashable {
159182 public static var scanningForValue : SingleValueParsingStrategy {
160183 self . init ( base: . scanningForValue)
161184 }
185+
186+ /// Parse the next input, as long as that input can't be interpreted as
187+ /// an option or flag, with a default value when the option is not provided.
188+ ///
189+ /// - Parameter defaultValue: The default value to use when the option is not provided.
190+ /// - Returns: A parsing strategy that scans for value with a default.
191+ public static func scanningForValue< T> ( default defaultValue: T )
192+ -> SingleValueParsingStrategy
193+ where T: ExpressibleByArgument {
194+ self . init (
195+ base: . scanningForValueWithDefault( defaultValue. defaultValueDescription) )
196+ }
162197}
163198
164199extension SingleValueParsingStrategy : Sendable { }
@@ -273,6 +308,11 @@ extension Option where Value: ExpressibleByArgument {
273308 help: ArgumentHelp ? = nil ,
274309 completion: CompletionKind ? = nil
275310 ) {
311+ // Validate that we don't have conflicting default values
312+ if case . scanningForValueWithDefault = parsingStrategy. base {
313+ fatalError ( Self . conflictingDefaultsErrorMessage)
314+ }
315+
276316 self . init (
277317 _parsedValue: . init { key in
278318 let arg = ArgumentDefinition (
@@ -307,6 +347,11 @@ extension Option where Value: ExpressibleByArgument {
307347 completion: CompletionKind ? ,
308348 help: ArgumentHelp ?
309349 ) {
350+ // Validate that we don't have conflicting default values
351+ if case . scanningForValueWithDefault = parsingStrategy. base {
352+ fatalError ( Self . conflictingDefaultsErrorMessage)
353+ }
354+
310355 self . init (
311356 wrappedValue: _wrappedValue,
312357 name: name,
@@ -336,7 +381,11 @@ extension Option where Value: ExpressibleByArgument {
336381 parsing parsingStrategy: SingleValueParsingStrategy = . next,
337382 help: ArgumentHelp ? = nil ,
338383 completion: CompletionKind ? = nil
339- ) {
384+ ) where Value: ExpressibleByArgument {
385+ // This initializer doesn't have a wrapped value, so scanningForValueWithDefault is allowed
386+ let defaultValue : Value ? = nil
387+ let actualParsingStrategy = parsingStrategy. base
388+
340389 self . init (
341390 _parsedValue: . init { key in
342391 let arg = ArgumentDefinition (
@@ -350,8 +399,8 @@ extension Option where Value: ExpressibleByArgument {
350399 visibility: help? . visibility ?? . default,
351400 argumentType: Value . self
352401 ) ,
353- parsingStrategy: parsingStrategy . base ,
354- initial: nil ,
402+ parsingStrategy: actualParsingStrategy ,
403+ initial: defaultValue ,
355404 completion: completion)
356405
357406 return ArgumentSet ( arg)
@@ -392,6 +441,11 @@ extension Option {
392441 completion: CompletionKind ? = nil ,
393442 transform: @Sendable @escaping ( String) throws -> Value
394443 ) {
444+ // Validate that we don't have conflicting default values
445+ if case . scanningForValueWithDefault = parsingStrategy. base {
446+ fatalError ( Self . conflictingDefaultsErrorMessage)
447+ }
448+
395449 self . init (
396450 _parsedValue: . init { key in
397451 let arg = ArgumentDefinition (
@@ -437,6 +491,7 @@ extension Option {
437491 completion: CompletionKind ? = nil ,
438492 transform: @Sendable @escaping ( String) throws -> Value
439493 ) {
494+ // This initializer doesn't have a wrapped value, so scanningForValueWithDefault is allowed
440495 self . init (
441496 _parsedValue: . init { key in
442497 let arg = ArgumentDefinition (
@@ -482,6 +537,7 @@ extension Option {
482537 help: ArgumentHelp ? = nil ,
483538 completion: CompletionKind ? = nil
484539 ) where T: ExpressibleByArgument , Value == T ? {
540+ // Note: This initializer uses nil as wrappedValue, so scanningForValueWithDefault is allowed
485541 self . init (
486542 _parsedValue: . init { key in
487543 let arg = ArgumentDefinition (
@@ -517,6 +573,13 @@ extension Option {
517573 help: ArgumentHelp ? = nil ,
518574 completion: CompletionKind ? = nil
519575 ) where T: ExpressibleByArgument , Value == T ? {
576+ // Validate that we don't have conflicting default values (only if wrappedValue is not nil)
577+ if _wrappedValue != nil ,
578+ case . scanningForValueWithDefault = parsingStrategy. base
579+ {
580+ fatalError ( Self . conflictingDefaultsErrorMessage)
581+ }
582+
520583 self . init (
521584 _parsedValue: . init { key in
522585 let arg = ArgumentDefinition (
@@ -560,6 +623,10 @@ extension Option {
560623 help: ArgumentHelp ? = nil ,
561624 completion: CompletionKind ? = nil
562625 ) where T: ExpressibleByArgument , Value == T ? {
626+ // This initializer doesn't have a wrapped value, so scanningForValueWithDefault is allowed
627+ let defaultValue : T ? = nil
628+ let actualParsingStrategy = parsingStrategy. base
629+
563630 self . init (
564631 _parsedValue: . init { key in
565632 let arg = ArgumentDefinition (
@@ -573,8 +640,8 @@ extension Option {
573640 visibility: help? . visibility ?? . default,
574641 argumentType: T . self
575642 ) ,
576- parsingStrategy: parsingStrategy . base ,
577- initial: nil ,
643+ parsingStrategy: actualParsingStrategy ,
644+ initial: defaultValue ,
578645 completion: completion)
579646
580647 return ArgumentSet ( arg)
@@ -615,6 +682,7 @@ extension Option {
615682 completion: CompletionKind ? = nil ,
616683 transform: @Sendable @escaping ( String) throws -> T
617684 ) where Value == T ? {
685+ // Note: This initializer uses nil as wrappedValue, so scanningForValueWithDefault is allowed
618686 self . init (
619687 _parsedValue: . init { key in
620688 let arg = ArgumentDefinition (
@@ -647,6 +715,13 @@ extension Option {
647715 completion: CompletionKind ? = nil ,
648716 transform: @Sendable @escaping ( String) throws -> T
649717 ) where Value == T ? {
718+ // Validate that we don't have conflicting default values (only if wrappedValue is not nil)
719+ if _wrappedValue != nil ,
720+ case . scanningForValueWithDefault = parsingStrategy. base
721+ {
722+ fatalError ( Self . conflictingDefaultsErrorMessage)
723+ }
724+
650725 self . init (
651726 _parsedValue: . init { key in
652727 let arg = ArgumentDefinition (
@@ -691,6 +766,7 @@ extension Option {
691766 completion: CompletionKind ? = nil ,
692767 transform: @Sendable @escaping ( String) throws -> T
693768 ) where Value == T ? {
769+ // This initializer doesn't have a wrapped value, so scanningForValueWithDefault is allowed
694770 self . init (
695771 _parsedValue: . init { key in
696772 let arg = ArgumentDefinition (
@@ -744,6 +820,7 @@ extension Option {
744820 help: ArgumentHelp ? = nil ,
745821 completion: CompletionKind ? = nil
746822 ) where T: ExpressibleByArgument , Value == [ T ] {
823+ // Note: Array parsing strategies don't include scanningForValueWithDefault, so no validation needed
747824 self . init (
748825 _parsedValue: . init { key in
749826 let arg = ArgumentDefinition (
@@ -851,6 +928,7 @@ extension Option {
851928 completion: CompletionKind ? = nil ,
852929 transform: @Sendable @escaping ( String) throws -> T
853930 ) where Value == [ T ] {
931+ // Note: Array parsing strategies don't include scanningForValueWithDefault, so no validation needed
854932 self . init (
855933 _parsedValue: . init { key in
856934 let arg = ArgumentDefinition (
0 commit comments