Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI flags/switches and subcommands #4

Open
alessiostalla opened this issue Jul 24, 2021 · 3 comments
Open

CLI flags/switches and subcommands #4

alessiostalla opened this issue Jul 24, 2021 · 3 comments

Comments

@alessiostalla
Copy link

We can easily use subcommands to support something like ./frob create thing, where "create" is the subcommand and "thing" is a rest argument. We can add flags/switches as well like this: ./frob create --beautiful thing. However, there's no built-in way (apparently) to process the following without analyzing the arguments manually (which defeats the purpose of the library): ./frob create thing --value 42.

I think a (hackish?) way of supporting this use case would be to always insert a terminating subcommand. So, the above would actually be processed as if the user had written ./frob create thing --value 42 now-please-do-it and "now-please-do-it" were a subcommand that inherited all the arguments of the "create" subcommand (inheritance which, by the way, is already possible, I don't know if by design or by accident, but I'm using it). And, if we use "--" instead of "now-please-do-it", and enable it for all subcommands, we also obtain the common pattern of using "--" to mean "please process everything after as REST arguments", e.g. in ./frob delete this that --gently -- --also-delete-this --this-is-not-a-flag this-is-not-a-subcommand.

If you're interested in the use case, and in the proposed solution design, I'll gladly provide a PR.

@svetlyak40wt
Copy link
Member

@alessiostalla I don't understand what are you proposing here?

Do you want to implement a -- support to pass rest flags unparsed?

@alessiostalla
Copy link
Author

alessiostalla commented Jul 27, 2021

Suppose I have this "frob" utility that connects to some server and issues commands. So I may have a main function that knows how to process parameters to connect to the server:

(defmain (frob) ((host "the host" :default "localhost") #| etc. |# &subcommand))

Then I define a subcommand to do some stuff, e.g.

(defcommand (frob create) (&rest args)
  "Creates a beautiful FROB"
  (frob:create (car args) :host host))

In the subcommand I can redefine the host, etc. args so that I may put them before or after the subcommand, but that's not shown here for simplicity.

Now I can invoke my utility with

./frob --host foo.com create my-frob

And with

./frob create --host foo.com my-frob

But not with

./frob create my-frob --host foo.com

Because in the latter case "--host foo.com" ends up in the &rest args and is not processed.

That is the issue.
The rest is the solution I was proposing, but I should have not mixed the two, sorry, as it is too much information.

@alessiostalla
Copy link
Author

alessiostalla commented Jul 27, 2021

The possible solution I propose is the following.
We can make

./frob create my-frob --host foo.com

work without changing the cli args processing logic if we add a dummy subcommand:

(defcommand (create do-it) () "Same as create")

However, the user would have to write

./frob create my-frob --host foo.com do-it

which defeats the purpose we had (i.e. being able to add args at the end).
However, we could preprocess the arg list returned by the Lisp implementation to always insert a "do-it" at the end, so effectively the user can type

./frob create my-frob --host foo.com

and the application interprets it as if they had typed

./frob create my-frob --host foo.com do-it

Also, defmain could automatically add this do-it subcommand to every command, so that the user does not have to do so manually. Then, if we name this special terminating subcommand "--" instead of do-it, we obtain the "-- means stop parsing" convention automatically – without altering how defmain or clon do the rest of their work.

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

No branches or pull requests

2 participants