Skip to content

Commit c0f9a5f

Browse files
authored
Allow normal Swift default property initialization syntax (#170)
* Allow normal Swift default property initialization syntax This change allows the normal `var foo = "blah"` default initialization syntax for `Option`s, as a parallel initialization method as using the `default` parameter. * Add simple tests for default property initialization * Centralize some constructor logic into a private `init` Preparing for another no-initial value `init` to be added and the existing one with a `default` parameter to be deprecated * Deprecate previous `Option.init` with `default` parameter It's replaced with an `init` containing no default value parameter, which will be used when the user does not provide any value. Also add a (most likely unnecessary) sanity test to make sure initializations without a default value still work. Also copy out documentation to allow clean removal of the older `init` when the time comes. * Document added test cases * Correct punctuation * Extend standard default initialization syntax to `Option`s with `transform` parameters * Actually replace previous `init` with private version This mirrors the non-transform variants, and should have been included in the previous commits * Clean up usage of default parameter values Private `init` doesn't need defaults, and the deprecated public ones shouldn't have it to avoid confusion with the new methods * Clean up documentation Treat new initializers as the originally intended way to allow for clean removal of the deprecated methods Also add some additional documentation to the deprecated methods to help point users in the right direction * Extend standard default initialization to `Argument`s * Extend standard default initialization to `Flag`s * Default flags with inversions to nil/required * Extend standard default initialization to no-inversion boolean `Flags` Prints a warning when that default value is `true` instead of `false`, as the flag value will be pinned regardless of user input * Eliminate deprecation spam from default value initialization All examples and unit tests have been transitioned to the new syntax, with the exception of `SourceCompatEndToEndTests`, which should not have the old style removed until it is no longer valid source. * Add source compatibility tests for new default syntax and associated changes * Update top-level documentation
1 parent f07d2fb commit c0f9a5f

32 files changed

+1463
-672
lines changed

Documentation/02 Arguments, Options, and Flags.md

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ The three preceding examples could be calls of this `Example` command:
2727
```swift
2828
struct Example: ParsableCommand {
2929
@Argument() var files: [String]
30-
30+
3131
@Option() var count: Int?
32-
33-
@Option(default: 0) var index: Int
34-
32+
33+
@Option var index: Int = 0
34+
3535
@Flag() var verbose: Bool
36-
36+
3737
@Flag() var stripWhitespace: Bool
3838
}
3939
```
@@ -51,7 +51,7 @@ Users must provide values for all properties with no implicit or specified defau
5151
struct Example: ParsableCommand {
5252
@Option()
5353
var userName: String
54-
54+
5555
@Argument()
5656
var value: Int
5757
}
@@ -105,13 +105,13 @@ You can override this default by specifying one or more name specifications in t
105105
struct Example: ParsableCommand {
106106
@Flag(name: .long) // Same as the default
107107
var stripWhitespace: Bool
108-
108+
109109
@Flag(name: .short)
110110
var verbose: Bool
111-
111+
112112
@Option(name: .customLong("count"))
113113
var iterationCount: Int
114-
114+
115115
@Option(name: [.customShort("I"), .long])
116116
var inputFile: String
117117
}
@@ -147,7 +147,7 @@ You can make your own custom types conform to `ExpressibleByArgument` by impleme
147147
```swift
148148
struct Path: ExpressibleByArgument {
149149
var pathString: String
150-
150+
151151
init?(argument: String) {
152152
self.pathString = argument
153153
}
@@ -167,7 +167,7 @@ enum ReleaseMode: String, ExpressibleByArgument {
167167

168168
struct Example: ParsableCommand {
169169
@Option() var mode: ReleaseMode
170-
170+
171171
mutating func run() throws {
172172
print(mode)
173173
}
@@ -189,7 +189,7 @@ To use a non-`ExpressibleByArgument` type for an argument or option, you can ins
189189
enum Format {
190190
case text
191191
case other(String)
192-
192+
193193
init(_ string: String) throws {
194194
if string == "text" {
195195
self = .text
@@ -209,23 +209,23 @@ Throw an error from the `transform` function to indicate that the user provided
209209

210210
## Using flag inversions, enumerations, and counts
211211

212-
Flags are most frequently used for `Bool` properties, with a default value of `false`. You can generate a `true`/`false` pair of flags by specifying a flag inversion:
212+
Flags are most frequently used for `Bool` properties. You can generate a `true`/`false` pair of flags by specifying a flag inversion:
213213

214214
```swift
215215
struct Example: ParsableCommand {
216-
@Flag(default: true, inversion: .prefixedNo)
217-
var index: Bool
216+
@Flag(inversion: .prefixedNo)
217+
var index: Bool = true
218218

219-
@Flag(default: nil, inversion: .prefixedEnableDisable)
219+
@Flag(inversion: .prefixedEnableDisable)
220220
var requiredElement: Bool
221-
221+
222222
mutating func run() throws {
223223
print(index, requiredElement)
224224
}
225225
}
226226
```
227227

228-
When providing a flag inversion, you can pass your own default as the `default` parameter. If you want to require that the user specify one of the two inversions, pass `nil` as the `default` parameter.
228+
When providing a flag inversion, you can pass your own default with normal property initialization syntax (`@Flag var foo: Bool = true`). If you want to require that the user specify one of the two inversions, use a non-Optional type and do not pass a default value.
229229

230230
In the `Example` command defined above, a flag is required for the `requiredElement` property. The specified prefixes are prepended to the long names for the flags:
231231

@@ -252,23 +252,23 @@ enum Color: EnumerableFlag {
252252

253253
struct Example: ParsableCommand {
254254
@Flag() var cacheMethod: CacheMethod
255-
255+
256256
@Flag() var colors: [Color]
257-
257+
258258
mutating func run() throws {
259259
print(cacheMethod)
260260
print(colors)
261261
}
262262
}
263-
```
263+
```
264264

265265
The flag names in this case are drawn from the raw values — for information about customizing the names and help text, see the [`EnumerableFlag` documentation](../Sources/ArgumentParser/Parsable%20Types/EnumerableFlag.swift).
266266

267267
```
268268
% example --in-memory-cache --pink --silver
269269
.inMemoryCache
270270
[.pink, .silver]
271-
% example
271+
% example
272272
Error: Missing one of: '--in-memory-cache', '--persistent-cache'
273273
```
274274

@@ -278,7 +278,7 @@ Finally, when a flag is of type `Int`, the value is parsed as a count of the num
278278
struct Example: ParsableCommand {
279279
@Flag(name: .shortAndLong)
280280
var verbose: Int
281-
281+
282282
mutating func run() throws {
283283
print("Verbosity level: \(verbose)")
284284
}
@@ -305,7 +305,7 @@ struct Example: ParsableCommand {
305305
@Flag() var verbose: Bool
306306
@Option() var name: String
307307
@Argument() var file: String?
308-
308+
309309
mutating func run() throws {
310310
print("Verbose: \(verbose), name: \(name), file: \(file ?? "none")")
311311
}
@@ -351,7 +351,7 @@ The default strategy for parsing options as arrays is to read each value from a
351351
struct Example: ParsableCommand {
352352
@Option() var file: [String]
353353
@Flag() var verbose: Bool
354-
354+
355355
mutating func run() throws {
356356
print("Verbose: \(verbose), files: \(file)")
357357
}
@@ -402,7 +402,7 @@ The default strategy for parsing arrays of positional arguments is to ignore al
402402
struct Example: ParsableCommand {
403403
@Flag() var verbose: Bool
404404
@Argument() var files: [String]
405-
405+
406406
mutating func run() throws {
407407
print("Verbose: \(verbose), files: \(files)")
408408
}

Documentation/03 Commands and Subcommands.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,25 +68,25 @@ It's time to define our first two subcommands: `Add` and `Multiply`. Both of the
6868
```swift
6969
extension Math {
7070
struct Add: ParsableCommand {
71-
static var configuration
71+
static var configuration
7272
= CommandConfiguration(abstract: "Print the sum of the values.")
7373

7474
@OptionGroup()
7575
var options: Math.Options
76-
76+
7777
mutating func run() {
7878
let result = options.values.reduce(0, +)
7979
print(format(result: result, usingHex: options.hexadecimalOutput))
8080
}
8181
}
8282

8383
struct Multiply: ParsableCommand {
84-
static var configuration
84+
static var configuration
8585
= CommandConfiguration(abstract: "Print the product of the values.")
8686

8787
@OptionGroup()
8888
var options: Math.Options
89-
89+
9090
mutating func run() {
9191
let result = options.values.reduce(1, *)
9292
print(format(result: result, usingHex: options.hexadecimalOutput))
@@ -98,7 +98,7 @@ extension Math {
9898
Next, we'll define `Statistics`, the third subcommand of `Math`. The `Statistics` command specifies a custom command name (`stats`) in its configuration, overriding the default derived from the type name (`statistics`). It also declares two additional subcommands, meaning that it acts as a forked branch in the command tree, and not a leaf.
9999

100100
```swift
101-
extension Math {
101+
extension Math {
102102
struct Statistics: ParsableCommand {
103103
static var configuration = CommandConfiguration(
104104
commandName: "stats",
@@ -115,21 +115,21 @@ extension Math.Statistics {
115115
struct Average: ParsableCommand {
116116
static var configuration = CommandConfiguration(
117117
abstract: "Print the average of the values.")
118-
118+
119119
enum Kind: String, ExpressibleByArgument {
120120
case mean, median, mode
121121
}
122122

123-
@Option(default: .mean, help: "The kind of average to provide.")
124-
var kind: Kind
125-
123+
@Option(help: "The kind of average to provide.")
124+
var kind: Kind = .mean
125+
126126
@Argument(help: "A group of floating-point values to operate on.")
127127
var values: [Double]
128-
128+
129129
func calculateMean() -> Double { ... }
130130
func calculateMedian() -> Double { ... }
131131
func calculateMode() -> [Double] { ... }
132-
132+
133133
mutating func run() {
134134
switch kind {
135135
case .mean:
@@ -144,15 +144,15 @@ extension Math.Statistics {
144144
}
145145
}
146146
}
147-
147+
148148
struct StandardDeviation: ParsableCommand {
149149
static var configuration = CommandConfiguration(
150150
commandName: "stdev",
151151
abstract: "Print the standard deviation of the values.")
152-
152+
153153
@Argument(help: "A group of floating-point values to operate on.")
154154
var values: [Double]
155-
155+
156156
mutating func run() {
157157
if values.isEmpty {
158158
print(0.0)

Documentation/04 Customizing Help.md

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22

33
Support your users (and yourself) by providing rich help for arguments and commands.
44

5-
You can provide help text when declaring any `@Argument`, `@Option`, or `@Flag` by passing a string literal as the `help` parameter:
5+
You can provide help text when declaring any `@Argument`, `@Option`, or `@Flag` by passing a string literal as the `help` parameter:
66

77
```swift
88
struct Example: ParsableCommand {
99
@Flag(help: "Display extra information while processing.")
1010
var verbose: Bool
11-
12-
@Option(default: 0, help: "The number of extra lines to show.")
13-
var extraLines: Int
14-
11+
12+
@Option(help: "The number of extra lines to show.")
13+
var extraLines: Int = 0
14+
1515
@Argument(help: "The input file.")
1616
var inputFile: String?
1717
}
@@ -24,10 +24,10 @@ Users see these strings in the automatically-generated help screen, which is tri
2424
USAGE: example [--verbose] [--extra-lines <extra-lines>] <input-file>
2525
2626
ARGUMENTS:
27-
<input-file> The input file.
27+
<input-file> The input file.
2828
2929
OPTIONS:
30-
--verbose Display extra information while processing.
30+
--verbose Display extra information while processing.
3131
--extra-lines <extra-lines>
3232
The number of extra lines to show. (default: 0)
3333
-h, --help Show help information.
@@ -43,12 +43,12 @@ Here's the same command with some extra customization:
4343
struct Example: ParsableCommand {
4444
@Flag(help: "Display extra information while processing.")
4545
var verbose: Bool
46-
47-
@Option(default: 0, help: ArgumentHelp(
46+
47+
@Option(help: ArgumentHelp(
4848
"The number of extra lines to show.",
4949
valueName: "n"))
50-
var extraLines: Int
51-
50+
var extraLines: Int = 0
51+
5252
@Argument(help: ArgumentHelp(
5353
"The input file.",
5454
discussion: "If no input file is provided, the tool reads from stdin.",
@@ -63,11 +63,11 @@ struct Example: ParsableCommand {
6363
USAGE: example [--verbose] [--extra-lines <n>] [<file>]
6464
6565
ARGUMENTS:
66-
<file> The input file.
66+
<file> The input file.
6767
If no input file is provided, the tool reads from stdin.
6868
6969
OPTIONS:
70-
--verbose Display extra information while processing.
70+
--verbose Display extra information while processing.
7171
--extra-lines <n> The number of extra lines to show. (default: 0)
7272
-h, --help Show help information.
7373
```
@@ -83,10 +83,10 @@ struct Repeat: ParsableCommand {
8383
discussion: """
8484
Prints to stdout forever, or until you halt the program.
8585
""")
86-
86+
8787
@Argument(help: "The phrase to repeat.")
8888
var phrase: String
89-
89+
9090
mutating func run() throws {
9191
while true { print(phrase) }
9292
}
@@ -104,7 +104,7 @@ Prints to stdout forever, or until you halt the program.
104104
USAGE: repeat <phrase>
105105
106106
ARGUMENTS:
107-
<phrase> The phrase to repeat.
107+
<phrase> The phrase to repeat.
108108
109109
OPTIONS:
110110
-h, --help Show help information.
@@ -127,10 +127,10 @@ Users can see the help screen for a command by passing either the `-h` or the `-
127127
struct Example: ParsableCommand {
128128
static let configuration = CommandConfiguration(
129129
helpNames: [.long, .customShort("?")])
130-
130+
131131
@Option(name: .shortAndLong, help: "The number of history entries to show.")
132132
var historyDepth: Int
133-
133+
134134
mutating func run() throws {
135135
printHistory(depth: historyDepth)
136136
}
@@ -146,7 +146,7 @@ When running the command, `-h` matches the short name of the `historyDepth` prop
146146
USAGE: example --history-depth <history-depth>
147147
148148
ARGUMENTS:
149-
<phrase> The phrase to repeat.
149+
<phrase> The phrase to repeat.
150150
151151
OPTIONS:
152152
-h, --history-depth The number of history entries to show.
@@ -155,7 +155,7 @@ OPTIONS:
155155

156156
## Hiding Arguments and Commands
157157

158-
You may want to suppress features under development or experimental flags from the generated help screen. You can hide an argument or a subcommand by passing `shouldDisplay: false` to the property wrapper or `CommandConfiguration` initializers, respectively.
158+
You may want to suppress features under development or experimental flags from the generated help screen. You can hide an argument or a subcommand by passing `shouldDisplay: false` to the property wrapper or `CommandConfiguration` initializers, respectively.
159159

160160
`ArgumentHelp` includes a `.hidden` static property that makes it even simpler to hide arguments:
161161

0 commit comments

Comments
 (0)