Coding 2024-08-29
Well, I feel like doing a bit more planning, since there isn't really a point in writing elaborate subcommand infrastructure if it can't actually parse things for one command. I'm calling this project the Terminal Argument Parser; it's inspired by the Command Line Interface Creation Kit. So, let's look over the Click documentation and figure out what to copy and what to do differently.
First major thing: Lua does not care if you do ludicrous higher-order function stuff, but there's no syntactic sugar comparable to Python's decorators, so let's punt on the exact shape the interface takes.
Let's see what to keep from options. Definitely want to be able to specific different option names. However, dispatching values based on argument name isn't a thing in Lua, so the function that ultimately handles this stuff is just going to have to take a "namespace" argument and get values from it using option names. I think the exact same strings used on the command line should work. Options can have defaults, sure. And some form of converter function. Can mark it as required. That seems like it should be mutually exclusive with providing a default... If there is a default, can choose whether to print it in the help. Flag-type values need a bit more thought.
These don't take any arguments on the command line, and should toggle between two states, usually true and false. If I'm committing to not having a "destination" concept, then there are two possible situations: Either there's one flag argument that switches from one arbitrary value to another, or there are paired flag arguments that have opposite boolean values. I feel a slight aesthetic affinity for the latter, but I don't have a sense of what this project is going to actually need, if anything.
Multi value options. Not compatible with flags. Aesthetically, they make me want to make "vanilla" converter functions "pervasive", applying the same mapping operation to every argument; specialized multi-value converters can then only work on specific numbers of arguments. Note that doing it like this would not set nargs, so perhaps there's an argument to be made for dispatching on whether the converter is passed as a function or an array. (Of course, it could be a functable, so maybe a table with a metatable should be treated as a function?)
Multiple options. I may have put myself into a bit of a corner. Here's what makes sense to me: If multiple options are supported, then that's handled by multiple returns. If multi values are supported, then the result of converting is somehow collected into a single table. So if you do both, then you get a bunch of tables.
Counting. Is a type of flag. I assume it could have a default value of some integer, but I'm not sure what that would accomplish that couldn't be done by just defaulting to zero and adding a number later.
Boolean flags goes over stuff I mentioned earlier...
Then we've got feature switches. By the reasoning I had above, Tap would have this as a set of flag values in which no more than one can be true at once.
Choice options... are a type of converter.
Prompting is another dimension of variation. I don't know how much of a pain it is to support the password-related features.
I guess stuff like show_default, eager parameters, and callbacks need to be a thing... Woof. Eager parameters imply, I think, that the stack of subcommands needs to be built up ahead of time. Which is doable, just, kind of a pain. No need to worry about an equivalent to expose_value I think; if you don't look for it, you won't see it.
Want to implement a yes parameter, then write a helper for it...
For environment variable stuff, the prefix has to be passed to the top-level call, and choosing an environment variable requires that Tap does choose a canonical name. I'm not so interested in automatically generating an environment variable name, as I am in specifying a static one, so I can do something like export CHAOS_FACTOR=5. That may be simply a dynamic default.
Skimming over arguments... These either need to be specified in a clear order via sequencing of calls, or simply passed in a list. Aside from that, most of the work to support arguments appears to be in custom converters.
I won't say all of this is simple—it frankly seems like a huge confusing tangle—but I've got a starting point for drawing a confusing diagram of how different options relate to each other, and that's really all I wanted for now. Anyway, I need to get ready for bed like five minutes ago.
Good night.