Skip to content

Commit 0bac2cc

Browse files
authored
Allow default nil values for optional properties (#480)
This adds underscored initializers that let library users add `= nil` to declarations of optional `@Option` and `@Argument` properties. Previously, default values have been available for properties of non-optional types only. These new initializers use `_OptionalNilComparisonType` as the wrapped value parameter, so only a `nil` literal is acceptable in the default value position. This avoids the problem of declaring an optional property with a non-`nil` default, which ends up negating the purpose of an optional.
1 parent 9f39744 commit 0bac2cc

File tree

12 files changed

+68
-14
lines changed

12 files changed

+68
-14
lines changed

Examples/count-lines/CountLines.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ struct CountLines: AsyncParsableCommand {
1818
@Argument(
1919
help: "A file to count lines in. If omitted, counts the lines of stdin.",
2020
completion: .file(), transform: URL.init(fileURLWithPath:))
21-
var inputFile: URL?
21+
var inputFile: URL? = nil
2222

2323
@Option(help: "Only count lines with this prefix.")
24-
var prefix: String?
24+
var prefix: String? = nil
2525

2626
@Flag(help: "Include extra information in the output.")
2727
var verbose = false

Examples/repeat/Repeat.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import ArgumentParser
1414
@main
1515
struct Repeat: ParsableCommand {
1616
@Option(help: "The number of times to repeat 'phrase'.")
17-
var count: Int?
17+
var count: Int? = nil
1818

1919
@Flag(help: "Include a counter with each repetition.")
2020
var includeCounter = false

Examples/roll/main.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct RollOptions: ParsableArguments {
2222
var sides = 6
2323

2424
@Option(help: "A seed to use for repeatable random generation.")
25-
var seed: Int?
25+
var seed: Int? = nil
2626

2727
@Flag(name: .shortAndLong, help: "Show all roll results.")
2828
var verbose = false

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ struct Repeat: ParsableCommand {
1717
var includeCounter = false
1818

1919
@Option(name: .shortAndLong, help: "The number of times to repeat 'phrase'.")
20-
var count: Int?
20+
var count: Int? = nil
2121

2222
@Argument(help: "The phrase to repeat.")
2323
var phrase: String

Sources/ArgumentParser/Documentation.docc/ArgumentParser.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct Repeat: ParsableCommand {
2121
var phrase: String
2222

2323
@Option(help: "The number of times to repeat 'phrase'.")
24-
var count: Int?
24+
var count: Int? = nil
2525

2626
mutating func run() throws {
2727
let repeatCount = count ?? 2

Sources/ArgumentParser/Documentation.docc/Articles/CustomizingCommandHelp.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct Repeat: ParsableCommand {
2222
var phrase: String
2323

2424
@Option(help: "How many times to repeat.")
25-
var count: Int?
25+
var count: Int? = nil
2626

2727
mutating func run() throws {
2828
for _ in 0..<(count ?? 2) {

Sources/ArgumentParser/Documentation.docc/Extensions/ParsableCommand.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ struct Repeat: ParsableCommand {
99
var phrase: String
1010

1111
@Option(help: "The number of times to repeat 'phrase'.")
12-
var count: Int?
12+
var count: Int? = nil
1313

1414
mutating func run() throws {
1515
let repeatCount = count ?? 2

Sources/ArgumentParser/Parsable Properties/Argument.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,19 @@ extension Argument {
234234
})
235235
}
236236

237+
/// This initializer allows a user to provide a `nil` default value for an
238+
/// optional `@Argument`-marked property without allowing a non-`nil` default
239+
/// value.
240+
public init<T: ExpressibleByArgument>(
241+
wrappedValue _value: _OptionalNilComparisonType,
242+
help: ArgumentHelp? = nil,
243+
completion: CompletionKind? = nil
244+
) where Value == T? {
245+
self.init(
246+
help: help,
247+
completion: completion)
248+
}
249+
237250
/// Creates a property with an optional default value, intended to be called by other constructors to centralize logic.
238251
///
239252
/// This private `init` allows us to expose multiple other similar constructors to allow for standard default property initialization while reducing code duplication.

Sources/ArgumentParser/Parsable Properties/Flag.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,24 @@ extension Flag where Value == Optional<Bool> {
219219
help: help)
220220
})
221221
}
222+
223+
/// This initializer allows a user to provide a `nil` default value for
224+
/// `@Flag`-marked `Optional<Bool>` property without allowing a non-`nil`
225+
/// default value.
226+
public init(
227+
wrappedValue _value: _OptionalNilComparisonType,
228+
name: NameSpecification = .long,
229+
inversion: FlagInversion,
230+
exclusivity: FlagExclusivity = .chooseLast,
231+
help: ArgumentHelp? = nil
232+
) {
233+
self.init(
234+
name: name,
235+
inversion: inversion,
236+
exclusivity: exclusivity,
237+
help: help)
238+
}
239+
222240
}
223241

224242
extension Flag where Value == Bool {

Sources/ArgumentParser/Parsable Properties/Option.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
/// @main
2626
/// struct Greet: ParsableCommand {
2727
/// @Option var greeting = "Hello"
28-
/// @Option var age: Int?
28+
/// @Option var age: Int? = nil
2929
/// @Option var name: String
3030
///
3131
/// mutating func run() {
@@ -361,6 +361,24 @@ extension Option {
361361
})
362362
}
363363

364+
/// This initializer allows a user to provide a `nil` default value for an
365+
/// optional `@Option`-marked property without allowing a non-`nil` default
366+
/// value.
367+
public init<T: ExpressibleByArgument>(
368+
wrappedValue _value: _OptionalNilComparisonType,
369+
name: NameSpecification = .long,
370+
parsing parsingStrategy: SingleValueParsingStrategy = .next,
371+
help: ArgumentHelp? = nil,
372+
completion: CompletionKind? = nil
373+
) where Value == T? {
374+
self.init(
375+
name: name,
376+
parsing: parsingStrategy,
377+
help: help,
378+
completion: completion
379+
)
380+
}
381+
364382
/// Creates a property with an optional default value, intended to be called by other constructors to centralize logic.
365383
///
366384
/// This private `init` allows us to expose multiple other similar constructors to allow for standard default property initialization while reducing code duplication.

0 commit comments

Comments
 (0)