I encountered some awkward ergonomics when migrating DrString to ArgumentParser.
tl;dr
I wish there's a way to to know whether a Flag's value is determined by actual presence in the command line arguments, or by default.
Details
Today, a user could supply the default value for a flag by either omitting it from command arguments, or explicitly supplying it. And as far as I know, there's no way to distinguish these two. The design space for CLI is, therefore, constrained such that we must not care about user's intention when it comes to these default values.
In my app, a user could specify an option in a config file, or they could do it as a command line argument. Crucially, when an option is present in both, the CLI value take precedence so that they can temporarily deviate from linting/formatting option of the codebase. To make this work, I have to use an @Option() flagName Bool? as opposed to @Flag() flagName: Bool to get the information I needed.
But that means the user must say --flag-name true as opposed to simply --flag-name, which is a regression in ergonomics.
I can't think of any obvious way to improve this in ArgumentParser. And, given that I've only worked with it for a few hours, it's entirely possible that I didn't think of/know of an obvious alternative. Thought I'd share here for some help/discussion :)
While I'm here: having used quite a few command line argument parsers from the Python and Swift community in the last 2 decades, I find ArgumentParser to be a delight to learn and use. The feature set is well rounded, API design thoughtful and clever. I'm more excited that ever for Swift's future in the CLI!
IIUC, the argument you're using is one of Flag type, where user only use the name to indicate presence, and in its absence, use the config file setting.
main --flag-name // true
main // config file
It would be the same as
@Flag(name: .custom(...))
var argFlagName
var flagName { self.argFlagName || self.config.flagName }
which doesn't seem to be parser concern to me.
Another way I could interpret this is as a mix of option/flag,
main --flag-name // true
main --flag-name=true // true
main --flag-name=false // false
main // config file
As I mentioned in my original post, that is the solution I had to go with and it has ergonomics issues because user now must supply true or false in command line.
No. The config file value is deliberately identical to the command line flags in my design. This is critical because there are a large number of possible arguments.
I'm still not quite seeing the problem here. A typical Boolean flag is an expression of whether the user specifies it on the command line or not. That is, if it's false, that means the user left it out, and if it's true, that means the user provided it.
The problem is I have a separate source (config file) for each of my flags. Let's say for a boolean flag "x" defined in ArgumentParser, the default value is false. In the config file, user marks the flag true, but they want to use CLI to override it.
Thinking in terms of code, how do I distinguish, let's say, cli --no-x and just cli? Because in either case, as far as x is concerned, the value is false according to ArgumentParser. But in my design, those two are different intentions from the user.