zio / zio-cli

Rapidly build powerful command-line applications powered by ZIO
https://zio.dev/zio-cli
Apache License 2.0
132 stars 78 forks source link

Add support for reading command-line options from file(s) (#191) #317

Open Kalin-Rudnicki opened 5 months ago

Kalin-Rudnicki commented 5 months ago

/claim #191

algora-pbc[bot] commented 5 months ago

💵 To receive payouts, sign up on Algora, link your Github account and connect with Stripe/Alipay.

Kalin-Rudnicki commented 5 months ago

Let me know if the code implementation is acceptable, and if so, I will add docs for it.

Kalin-Rudnicki commented 5 months ago

@pablf ccing you here because wasnt able to properly @ you in discord

pablf commented 5 months ago

@Kalin-Rudnicki Hi, it looks good! Just in case, the docs refer to the docs displayed by the CLI, not the library documentation. I like the flexibility of allowing to add your custom FileArgs. If you could add some comment about this function either as a comment, maybe in runWithFileArgs, or in the docs of the library, that would be great!

Kalin-Rudnicki commented 5 months ago

@pablf can you please advise where you would recommend adding notes about FileOptions to the cli help docs?

Kalin-Rudnicki commented 5 months ago

@pablf After trying to add tests for more complex commands, I realized that I think its sub-par to do command.names.headOption in order to decide what .command file to look for.

Because you could have something like:

command1 | command2

and then you would be looking for an indeterminate one of .command1 and .command2, irrelevant of which path it tries to parse.

Im not super familiar with this library, but IMO, it seems to me that Subcommands[A, B](parent: Command[A], is too loose on the typing.
Ive written my own CLI library, and I went a much simpler approach, where my version of a Command was either exclusively a Leaf or a Branch. Aka: going down a sub-command does no parsing, other than popping a command off the front of the args list.
TBH, I think this is a better approach, but would be a pretty big change to the lib...
It seems confusing to me to figure out what it would mean to parse SubCommand where parent has non-empty Args. I'm assuming it would behave something like this?
base-cmd base-cmd--arg-1 base-cmd--arg-1 sub-cmd-1 sub-cmd-2 sub-cmd-2--arg-1

As I mentioned, this would be a pretty large change, and I don't really want to go through that, even if you agreed it was a better model.
That being said, do you think it would be fair to make it so that only Single can have .subCommands? (requires a little more than that, in order to handle .map(???).subCommands(???), a sort of SingleLike)
This would make it much easier to determine what the root command was.

Also, it seems a bit strange to do something like

val cmd1: Command[?] = ??? // complex command with orElse and subCommands
val cmd2: Command[?] = ??? // complex command with orElse and subCommands

(cmd1 | cmd2).subCommands(???)
cmd1.subCommands(cmd2).subCommands(???)
pablf commented 5 months ago

@Kalin-Rudnicki The cli library parse and shows the help command in CliApp.scala I think, inside def run. Then it should also show which file options were used, if I remember well. This probably would be added also in method run of CliApp.

Regarding the design of Subcommand, I think both ways can be reasonable, although the actual one seems more flexible. It would allow to have things like:

val cmd1 = ???
val cmd2 = ???
val cmd3 = ???
val cmd4 = ???
val cmd = (cmd1 | cmd2).subcommands(cmd3 | cmd4)

so you can use all the following commands:

cmd1 cmd3
cmd1 cmd4
cmd2 cmd3
cmd2 cmd4

I think that the best way to implement this feature given the situation is to parse the command and, once you know which one is the top-level command, to retrieve the file options. This or just to allow this feature for commands with an unique top-level command. Maybe @jdegoes wants to share his thoughts on this.

Kalin-Rudnicki commented 4 months ago

Im going to pursue the unique top level option

Kalin-Rudnicki commented 4 months ago

Does this seem reasonable, or do you have any recommendations of how to phrase it more clearly/concisely:

USAGE

  $ test-command --text <text>

  File options will parsed from files named '.test-command' in the following places:
    - current working directory
    - all parent directories of the current working directory
    - home directory

OPTIONS

  --text <text>
    A user-defined piece of text.