2121/// `verbose` has a default value of `false`, but becomes `true` if `--verbose`
2222/// is provided on the command line.
2323///
24- /// A flag can have a value that is a `Bool`, an `Int`, or any `CaseIterable `
25- /// type. When using a `CaseIterable ` type as a flag, the individual cases
24+ /// A flag can have a value that is a `Bool`, an `Int`, or any `EnumerableFlag `
25+ /// type. When using an `EnumerableFlag ` type as a flag, the individual cases
2626/// form the flags that are used on the command line.
2727///
2828/// struct Options {
29- /// enum Operation: CaseIterable, ... {
29+ /// enum Operation: EnumerableFlag {
3030/// case add
3131/// case multiply
3232/// }
@@ -188,7 +188,9 @@ extension Flag where Value == Bool {
188188 ///
189189 /// - Parameters:
190190 /// - name: A specification for what names are allowed for this flag.
191- /// - initial: The default value for this flag.
191+ /// - initial: A default value to use for this property. If `initial` is
192+ /// `nil`, one of the flags declared by this `@Flag` attribute is required
193+ /// from the user.
192194 /// - inversion: The method for converting this flag's name into an on/off
193195 /// pair.
194196 /// - exclusivity: The behavior to use when an on/off pair of flags is
@@ -226,18 +228,20 @@ extension Flag where Value == Int {
226228 }
227229}
228230
229- extension Flag where Value: CaseIterable , Value: Equatable , Value: RawRepresentable , Value. RawValue == String {
231+ // - MARK: EnumerableFlag
232+
233+ extension Flag where Value: EnumerableFlag {
230234 /// Creates a property that gets its value from the presence of a flag,
231- /// where the allowed flags are defined by a `CaseIterable ` type.
235+ /// where the allowed flags are defined by an `EnumerableFlag ` type.
232236 ///
233237 /// - Parameters:
234238 /// - name: A specification for what names are allowed for this flag.
235239 /// - initial: A default value to use for this property. If `initial` is
236- /// `nil`, this flag is required.
240+ /// `nil`, one of the flags declared by this `@Flag` attribute is required
241+ /// from the user.
237242 /// - exclusivity: The behavior to use when multiple flags are specified.
238243 /// - help: Information about how to use this flag.
239244 public init (
240- name: NameSpecification = . long,
241245 default initial: Value ? = nil ,
242246 exclusivity: FlagExclusivity = . exclusive,
243247 help: ArgumentHelp ? = nil
@@ -248,9 +252,14 @@ extension Flag where Value: CaseIterable, Value: Equatable, Value: RawRepresenta
248252 var hasUpdated = false
249253 let defaultValue = initial. map ( String . init ( describing: ) )
250254
251- let args = Value . allCases. map { value -> ArgumentDefinition in
252- let caseKey = InputKey ( rawValue: value. rawValue)
253- let help = ArgumentDefinition . Help ( options: initial != nil ? . isOptional : [ ] , help: help, defaultValue: defaultValue, key: key)
255+ let caseHelps = Value . allCases. map { Value . help ( for: $0) }
256+ let hasCustomCaseHelp = caseHelps. contains ( where: { $0 != nil } )
257+
258+ let args = Value . allCases. enumerated ( ) . map { ( i, value) -> ArgumentDefinition in
259+ let caseKey = InputKey ( rawValue: String ( describing: value) )
260+ let name = Value . name ( for: value)
261+ let helpForCase = hasCustomCaseHelp ? ( caseHelps [ i] ?? help) : help
262+ let help = ArgumentDefinition . Help ( options: initial != nil ? . isOptional : [ ] , help: helpForCase, defaultValue: defaultValue, key: key, isComposite: !hasCustomCaseHelp)
254263 return ArgumentDefinition . flag ( name: name, key: key, caseKey: caseKey, help: help, parsingStrategy: . nextAsValue, initialValue: initial, update: . nullary( { ( origin, name, values) in
255264 hasUpdated = try ArgumentSet . updateFlag ( key: key, value: value, origin: origin, values: & values, hasUpdated: hasUpdated, exclusivity: exclusivity)
256265 } ) )
@@ -264,15 +273,110 @@ extension Flag where Value: CaseIterable, Value: Equatable, Value: RawRepresenta
264273
265274extension Flag {
266275 /// Creates a property that gets its value from the presence of a flag,
267- /// where the allowed flags are defined by a `CaseIterable` type.
276+ /// where the allowed flags are defined by an `EnumerableFlag` type.
277+ public init < Element> (
278+ exclusivity: FlagExclusivity = . exclusive,
279+ help: ArgumentHelp ? = nil
280+ ) where Value == Element ? , Element: EnumerableFlag {
281+ self . init ( _parsedValue: . init { key in
282+ // This gets flipped to `true` the first time one of these flags is
283+ // encountered.
284+ var hasUpdated = false
285+
286+ let caseHelps = Element . allCases. map { Element . help ( for: $0) }
287+ let hasCustomCaseHelp = caseHelps. contains ( where: { $0 != nil } )
288+
289+ let args = Element . allCases. enumerated ( ) . map { ( i, value) -> ArgumentDefinition in
290+ let caseKey = InputKey ( rawValue: String ( describing: value) )
291+ let name = Element . name ( for: value)
292+ let helpForCase = hasCustomCaseHelp ? ( caseHelps [ i] ?? help) : help
293+ let help = ArgumentDefinition . Help ( options: . isOptional, help: helpForCase, key: key, isComposite: !hasCustomCaseHelp)
294+ return ArgumentDefinition . flag ( name: name, key: key, caseKey: caseKey, help: help, parsingStrategy: . nextAsValue, initialValue: nil as Element ? , update: . nullary( { ( origin, name, values) in
295+ hasUpdated = try ArgumentSet . updateFlag ( key: key, value: value, origin: origin, values: & values, hasUpdated: hasUpdated, exclusivity: exclusivity)
296+ } ) )
297+
298+ }
299+ return exclusivity == . exclusive
300+ ? ArgumentSet ( exclusive: args)
301+ : ArgumentSet ( additive: args)
302+ } )
303+ }
304+
305+ /// Creates an array property that gets its values from the presence of
306+ /// zero or more flags, where the allowed flags are defined by an
307+ /// `EnumerableFlag` type.
268308 ///
269- /// This property has a default value of `nil`; specifying the flag in the
270- /// command-line arguments is not required.
309+ /// This property has an empty array as its default value.
271310 ///
272311 /// - Parameters:
273312 /// - name: A specification for what names are allowed for this flag.
313+ /// - help: Information about how to use this flag.
314+ public init < Element> (
315+ help: ArgumentHelp ? = nil
316+ ) where Value == Array < Element > , Element: EnumerableFlag {
317+ self . init ( _parsedValue: . init { key in
318+ let caseHelps = Element . allCases. map { Element . help ( for: $0) }
319+ let hasCustomCaseHelp = caseHelps. contains ( where: { $0 != nil } )
320+
321+ let args = Element . allCases. enumerated ( ) . map { ( i, value) -> ArgumentDefinition in
322+ let caseKey = InputKey ( rawValue: String ( describing: value) )
323+ let name = Element . name ( for: value)
324+ let helpForCase = hasCustomCaseHelp ? ( caseHelps [ i] ?? help) : help
325+ let help = ArgumentDefinition . Help ( options: . isOptional, help: helpForCase, key: key, isComposite: !hasCustomCaseHelp)
326+ return ArgumentDefinition . flag ( name: name, key: key, caseKey: caseKey, help: help, parsingStrategy: . nextAsValue, initialValue: [ Element] ( ) , update: . nullary( { ( origin, name, values) in
327+ values. update ( forKey: key, inputOrigin: origin, initial: [ Element] ( ) , closure: {
328+ $0. append ( value)
329+ } )
330+ } ) )
331+ }
332+ return ArgumentSet ( additive: args)
333+ } )
334+ }
335+ }
336+
337+ // - MARK: Deprecated CaseIterable/RawValue == String
338+
339+ extension Flag where Value: CaseIterable , Value: RawRepresentable , Value: Equatable , Value. RawValue == String {
340+ /// Creates a property that gets its value from the presence of a flag,
341+ /// where the allowed flags are defined by a case-iterable type.
342+ ///
343+ /// - Parameters:
344+ /// - name: A specification for what names are allowed for this flag.
345+ /// - initial: A default value to use for this property. If `initial` is
346+ /// `nil`, this flag is required.
274347 /// - exclusivity: The behavior to use when multiple flags are specified.
275348 /// - help: Information about how to use this flag.
349+ @available ( * , deprecated, message: " Add 'EnumerableFlag' conformance to your value type. " )
350+ public init (
351+ name: NameSpecification = . long,
352+ default initial: Value ? = nil ,
353+ exclusivity: FlagExclusivity = . exclusive,
354+ help: ArgumentHelp ? = nil
355+ ) {
356+ self . init ( _parsedValue: . init { key in
357+ // This gets flipped to `true` the first time one of these flags is
358+ // encountered.
359+ var hasUpdated = false
360+ let defaultValue = initial. map ( String . init ( describing: ) )
361+
362+ let args = Value . allCases. map { value -> ArgumentDefinition in
363+ let caseKey = InputKey ( rawValue: value. rawValue)
364+ let help = ArgumentDefinition . Help ( options: initial != nil ? . isOptional : [ ] , help: help, defaultValue: defaultValue, key: key, isComposite: true )
365+ return ArgumentDefinition . flag ( name: name, key: key, caseKey: caseKey, help: help, parsingStrategy: . nextAsValue, initialValue: initial, update: . nullary( { ( origin, name, values) in
366+ hasUpdated = try ArgumentSet . updateFlag ( key: key, value: value, origin: origin, values: & values, hasUpdated: hasUpdated, exclusivity: exclusivity)
367+ } ) )
368+ }
369+ return exclusivity == . exclusive
370+ ? ArgumentSet ( exclusive: args)
371+ : ArgumentSet ( additive: args)
372+ } )
373+ }
374+ }
375+
376+ extension Flag {
377+ /// Creates a property that gets its value from the presence of a flag,
378+ /// where the allowed flags are defined by a case-iterable type.
379+ @available ( * , deprecated, message: " Add 'EnumerableFlag' conformance to your value type. " )
276380 public init < Element> (
277381 name: NameSpecification = . long,
278382 exclusivity: FlagExclusivity = . exclusive,
@@ -285,15 +389,15 @@ extension Flag {
285389
286390 let args = Element . allCases. map { value -> ArgumentDefinition in
287391 let caseKey = InputKey ( rawValue: value. rawValue)
288- let help = ArgumentDefinition . Help ( options: . isOptional, help: help, key: key)
392+ let help = ArgumentDefinition . Help ( options: . isOptional, help: help, key: key, isComposite : true )
289393 return ArgumentDefinition . flag ( name: name, key: key, caseKey: caseKey, help: help, parsingStrategy: . nextAsValue, initialValue: nil as Element ? , update: . nullary( { ( origin, name, values) in
290394 hasUpdated = try ArgumentSet . updateFlag ( key: key, value: value, origin: origin, values: & values, hasUpdated: hasUpdated, exclusivity: exclusivity)
291395 } ) )
292396 }
293397 return exclusivity == . exclusive
294398 ? ArgumentSet ( exclusive: args)
295399 : ArgumentSet ( additive: args)
296- } )
400+ } )
297401 }
298402
299403 /// Creates an array property that gets its values from the presence of
@@ -305,22 +409,23 @@ extension Flag {
305409 /// - Parameters:
306410 /// - name: A specification for what names are allowed for this flag.
307411 /// - help: Information about how to use this flag.
412+ @available ( * , deprecated, message: " Add 'EnumerableFlag' conformance to your value type. " )
308413 public init < Element> (
309414 name: NameSpecification = . long,
310415 help: ArgumentHelp ? = nil
311416 ) where Value == Array < Element > , Element: CaseIterable , Element: RawRepresentable , Element. RawValue == String {
312417 self . init ( _parsedValue: . init { key in
313418 let args = Element . allCases. map { value -> ArgumentDefinition in
314419 let caseKey = InputKey ( rawValue: value. rawValue)
315- let help = ArgumentDefinition . Help ( options: . isOptional, help: help, key: key)
420+ let help = ArgumentDefinition . Help ( options: . isOptional, help: help, key: key, isComposite : true )
316421 return ArgumentDefinition . flag ( name: name, key: key, caseKey: caseKey, help: help, parsingStrategy: . nextAsValue, initialValue: [ Element] ( ) , update: . nullary( { ( origin, name, values) in
317422 values. update ( forKey: key, inputOrigin: origin, initial: [ Element] ( ) , closure: {
318423 $0. append ( value)
319424 } )
320425 } ) )
321426 }
322427 return ArgumentSet ( additive: args)
323- } )
428+ } )
324429 }
325430}
326431
0 commit comments