diff --git a/Sources/ArgumentParser/Parsable Properties/ArgumentDiscussion.swift b/Sources/ArgumentParser/Parsable Properties/ArgumentDiscussion.swift index 00101c47..46f2ad1d 100644 --- a/Sources/ArgumentParser/Parsable Properties/ArgumentDiscussion.swift +++ b/Sources/ArgumentParser/Parsable Properties/ArgumentDiscussion.swift @@ -77,9 +77,29 @@ /// -h, --help Show help information /// ``` /// -/// In any case where the argument type is not `EnumerableOptionValue`, the -/// default implementation will use the `.staticText` case and will print a -/// block of discussion text. +/// Arrays of enumerable types are also supported and will display the same +/// enumerated format: +/// +/// ```swift +/// @Option var colors: [Color] = [.red, .blue] +/// ``` +/// +/// The printed usage would look like the following: +/// +/// ``` +/// USAGE: example [--colors ...] +/// +/// OPTIONS: +/// --colors (default: red, blue) +/// red - A red color. +/// blue - A blue color. +/// yellow - A yellow color. +/// -h, --help Show help information +/// ``` +/// +/// In any case where the argument type is not `EnumerableOptionValue` or an +/// array of `EnumerableOptionValue`, the default implementation will use the +/// `.staticText` case and will print a block of discussion text. enum ArgumentDiscussion { case staticText(String) case enumerated(preamble: String? = nil, any ExpressibleByArgument.Type) diff --git a/Sources/ArgumentParser/Parsable Properties/Option.swift b/Sources/ArgumentParser/Parsable Properties/Option.swift index 0a5537e5..024a0232 100644 --- a/Sources/ArgumentParser/Parsable Properties/Option.swift +++ b/Sources/ArgumentParser/Parsable Properties/Option.swift @@ -721,6 +721,11 @@ extension Option { /// var chars: [Character] = [] /// ``` /// + /// If the element type conforms to `ExpressibleByArgument` and has enumerable + /// value descriptions (via `defaultValueDescription`), the help output will + /// display each possible value with its description, similar to single + /// enumerable options. + /// /// - Parameters: /// - wrappedValue: A default value to use for this property, provided /// implicitly by the compiler during property wrapper initialization. @@ -745,7 +750,13 @@ extension Option { container: Array.self, key: key, kind: .name(key: key, specification: name), - help: help, + help: .init( + help?.abstract ?? "", + discussion: help?.discussion, + valueName: help?.valueName, + visibility: help?.visibility ?? .default, + argumentType: T.self + ), parsingStrategy: parsingStrategy.base, initial: wrappedValue, completion: completion) @@ -765,6 +776,11 @@ extension Option { /// var chars: [Character] /// ``` /// + /// If the element type conforms to `ExpressibleByArgument` and has enumerable + /// value descriptions (via `defaultValueDescription`), the help output will + /// display each possible value with its description, similar to single + /// enumerable options. + /// /// - Parameters: /// - name: A specification for what names are allowed for this option. /// - parsingStrategy: The behavior to use when parsing the elements for @@ -784,7 +800,13 @@ extension Option { container: Array.self, key: key, kind: .name(key: key, specification: name), - help: help, + help: .init( + help?.abstract ?? "", + discussion: help?.discussion, + valueName: help?.valueName, + visibility: help?.visibility ?? .default, + argumentType: T.self + ), parsingStrategy: parsingStrategy.base, initial: nil, completion: completion) diff --git a/Sources/ArgumentParser/Parsing/ArgumentDefinition.swift b/Sources/ArgumentParser/Parsing/ArgumentDefinition.swift index 72e3784d..b59aa80a 100644 --- a/Sources/ArgumentParser/Parsing/ArgumentDefinition.swift +++ b/Sources/ArgumentParser/Parsing/ArgumentDefinition.swift @@ -447,7 +447,12 @@ where Element: ExpressibleByArgument { guard !initial.isEmpty else { return nil } return initial .lazy - .map { $0.defaultValueDescription } + .map { element in + if let element = element as? (any CaseIterable & RawRepresentable) { + return String(describing: element.rawValue) + } + return element.defaultValueDescription + } .joined(separator: ", ") } } diff --git a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift index fa0a2c6b..cc3973c7 100644 --- a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift +++ b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift @@ -1095,6 +1095,80 @@ extension HelpGenerationTests { """) } + struct CustomOptionAsListWithSingleDefaultValue: ParsableCommand { + @Option( + help: "An option with enumerable values.") + var opt: [OptionValues] = [.red] + } + + func testEnumerableOptionAsListWithSingleDefault() { + AssertHelp( + .default, + for: CustomOptionAsListWithSingleDefaultValue.self, + columns: 100, + equals: """ + USAGE: custom-option-as-list-with-single-default-value [--opt ...] + + OPTIONS: + --opt An option with enumerable values. (default: red) + blue - The color of the sky. + red - The color of a rose. + yellow - The color of the sun. + -h, --help Show help information. + + """) + } + + struct CustomOptionAsListWithMultipleDefaultValue: ParsableCommand { + @Option( + help: "An option with multiple enumerable values.") + var opt: [OptionValues] = [.red, .blue] + } + + func testEnumerableOptionAsListWithMultipleDefault() { + AssertHelp( + .default, + for: CustomOptionAsListWithMultipleDefaultValue.self, + columns: 100, + equals: """ + USAGE: custom-option-as-list-with-multiple-default-value [--opt ...] + + OPTIONS: + --opt An option with multiple enumerable values. (default: red, blue) + blue - The color of the sky. + red - The color of a rose. + yellow - The color of the sun. + -h, --help Show help information. + + """) + + } + + struct CustomOptionAsListWithEmptyArrayAsDefault: ParsableCommand { + @Option( + help: "An option with default value set to empty array.") + var opt: [OptionValues] = [] + } + + func testEnumerableOptionAsListWithEmptyArrayAsDefault() { + AssertHelp( + .default, + for: CustomOptionAsListWithEmptyArrayAsDefault.self, + columns: 100, + equals: """ + USAGE: custom-option-as-list-with-empty-array-as-default [--opt ...] + + OPTIONS: + --opt An option with default value set to empty array. + blue - The color of the sky. + red - The color of a rose. + yellow - The color of the sun. + -h, --help Show help information. + + """) + + } + struct CustomOptionWithDefault: ParsableCommand { @Option(help: "An option with enumerable values and a custom default.") var opt: OptionValues = .red