Skip to content

Conversation

@bkhouri
Copy link
Contributor

@bkhouri bkhouri commented Nov 21, 2025

In some use cases, there is a need to have an option argument behave like a flag.

This change introduced 4 new intialiazers to Option that accept a defaultAsFlag value.

With the following usage:

struct Example: ParsableCommand {
    @Option(defaultAsFlag: "default", help: "Set output format.")
    var format: String?
    func run() {
        print("Format: \(format ?? "none")")
    }
}

`

The defaultAsFlag parameter creates a hybrid that supports both patterns:

  • Flag behavior: --format (sets format to "default")
  • Option behavior: --format json (sets format to "json")
  • No usage: format remains nil

As a user of the command line tool, the --help output clearly distinguishes between the the hybrid and regular usages.

OPTIONS:
  --format [<format>]     Set output format. (default as flag: json)

Note the (default as flag: ...) text instead of regular (default: ...), and the optional value syntax [<value>] instead of required <value>.

Fixes: #829

Checklist

  • I've added at least one test that validates that my change is working, if appropriate
  • I've followed the code style of the rest of the project
  • I've read the Contribution Guidelines
  • I've updated the documentation if necessary

@bkhouri bkhouri force-pushed the t/main/gh829_add_new_argument_type_FlagOption branch 2 times, most recently from d08351c to 61a6602 Compare November 21, 2025 03:08
rauhul
rauhul previously requested changes Nov 21, 2025
Copy link
Contributor

@rauhul rauhul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • xctest instead of swift-testing to support swift 5.7
  • synopsis: [--log-level [log-level]] or [--log-level [<log-level>]]
  • dump-help snapshot tests
  • completion snapshot tests
  • manpage support
  • manpage snapshot tests
  • documentation

@rgoldberg
Copy link
Contributor

rgoldberg commented Nov 21, 2025

Would it make more sense instead to provide this functionality via Option? Like:

@Option(parsing: .scanningForValue(default: "text"))
var showBinPath: String?

You'd add/modify the following (I haven't checked the code for proper generics/types/compilation/keywords/etc.; it's just pseudo-code to illustrate the concept. I also haven't checked to see what problems adding the new SingleValueParsingStrategy to Option might cause):

public struct SingleValueParsingStrategy: Hashable {
  
  // This is an addition
  public static func scanningForValue<Value>(default: Value) -> SingleValueParsingStrategy {
    self.init(base: .scanningForValue(default: default))
  }
  
}

struct ArgumentDefinition {enum ParsingStrategy {
    …
    // This is a modification
    case scanningForValue(default: Any? = nil)
    
  }
  
}

One possible issue: must ensure that the option terminator -- ensures that json in the following command line is interpreted as positional argument 1, not as a value for --show-bin-path, but that problem might exist for this any FlagOption solution, too:

cmd --show-bin-path -- json

@bkhouri
Copy link
Contributor Author

bkhouri commented Nov 21, 2025

Thanks for the feedback @rgoldberg . What you proposed makes more sense than what I implemented.

@bkhouri bkhouri marked this pull request as draft November 21, 2025 17:29
@bkhouri bkhouri force-pushed the t/main/gh829_add_new_argument_type_FlagOption branch from 61a6602 to b319a92 Compare December 1, 2025 18:33
@bkhouri bkhouri requested a review from rauhul December 1, 2025 18:34
@bkhouri bkhouri changed the title Add new FlagOption type Add default value for Option Dec 1, 2025
@bkhouri bkhouri changed the title Add default value for Option Augment Option to support default value Dec 1, 2025
@bkhouri bkhouri force-pushed the t/main/gh829_add_new_argument_type_FlagOption branch from b319a92 to 2316d12 Compare December 1, 2025 18:43
@bkhouri bkhouri marked this pull request as ready for review December 1, 2025 18:43
@bkhouri bkhouri marked this pull request as draft December 10, 2025 17:15
@bkhouri
Copy link
Contributor Author

bkhouri commented Dec 10, 2025

After discussing with @rauhul and @natecook1000 , we came up with the following API

New initializers

init<T>(wrappedValue: _OptionalNilComparisonType, name: NameSpecification, defaultAsFlag: T, parsing: SingleValueParsingStrategy, help: ArgumentHelp?, completion: CompletionKind?)
init<T>(wrappedValue: _OptionalNilComparisonType, name: NameSpecification, defaultAsFlag: T, parsing: SingleValueParsingStrategy, help: ArgumentHelp?, completion: CompletionKind?, transform: (String) throws -> T)
init<T>(name: NameSpecification, defaultAsFlag: T, parsing: SingleValueParsingStrategy, help: ArgumentHelp?, completion: CompletionKind?)
init<T>(name: NameSpecification, defaultAsFlag: T, parsing: SingleValueParsingStrategy, help: ArgumentHelp?, completion: CompletionKind?, transform: (String) throws -> T)

API usage looks as follows

@Option(name: .long("bin-path"), defaultAsFlag: FilePath("/default/path"))
var showBinPath: FilePath?

And the help dump looks like:

OVERVIEW: cli overview
USAGE: myBin [--bin-path [<path>]] 
OPTIONS:
  -b, --bin-path [<path>] argument help text. (default as flag: "/default/path")
  -h, --help              Show help information.

@bkhouri bkhouri force-pushed the t/main/gh829_add_new_argument_type_FlagOption branch from 2316d12 to d3593e3 Compare December 11, 2025 04:36
@bkhouri bkhouri changed the title Augment Option to support default value Augment Option to support default as flag option Dec 11, 2025
@bkhouri bkhouri force-pushed the t/main/gh829_add_new_argument_type_FlagOption branch from d3593e3 to 7f148ea Compare December 11, 2025 21:13
In some use cases, there is a need to have an option argument behave
like a flag.

This change introduced 4 new intialiazers to `Option` that accept a
`defaultAsFlag` value.

With the following usage:

```swift
struct Example: ParsableCommand {
    @option(defaultAsFlag: "default", help: "Set output format.")
    var format: String?
    func run() {
        print("Format: \(format ?? "none")")
    }
}
```
`

The `defaultAsFlag` parameter creates a hybrid that supports both patterns:
  - **Flag behavior**: `--format` (sets format to "default")
  - **Option behavior**: `--format json` (sets format to "json")
  - **No usage**: format remains `nil`

As a user of the command line tool, the `--help` output clearly distinguishes
between the the hybrid and regular usages.

```
OPTIONS:
  --format [<format>]     Set output format. (default as flag: json)
````

Note the `(default as flag: ...)` text instead of regular `(default: ...)`,
and the optional value syntax `[<value>]` instead of required `<value>`.

Fixes: #829
@bkhouri bkhouri force-pushed the t/main/gh829_add_new_argument_type_FlagOption branch from 7f148ea to b84708e Compare December 11, 2025 21:24
@bkhouri bkhouri dismissed rauhul’s stale review December 12, 2025 01:21

The changes requested have been taken into account in the most up to date change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support an argument as a flag or option

4 participants