c-blake / cligen

Nim library to infer/generate command-line-interfaces / option / argument parsing; Docs at
https://c-blake.github.io/cligen/
ISC License
509 stars 24 forks source link

how to call usage from proc body #116

Closed lordvlad closed 5 years ago

lordvlad commented 5 years ago

not an issue really, I just can't figure it out, I guess. Since cligen currently does not support non-optional positional arguments as far as i can tell, I'm using something along the lines

configure(args: seq[string])=
  case args.len():
    of 1: get_config(args[0])
    of 2: set_config(args[0], args[1])
    else: raise newException(Exception, "Too many arguments")

What I want to do instead of only raising the error is printing the usage for the configure subcommand before exiting with exitCode 1.

c-blake commented 5 years ago

The short answer is cligen's usage probably will not be the most appropriate thing to print.

A big part of the time-saving/convenience of cligen is to autogenerate its usage based on some syntax. It seems like you are creating your own sub-syntax within args. That is fine as far as it goes, but to explain that sub-syntax you will have to build your own usage message.

Indeed, if you are only ever operating on args: seq[string], cligen may provide very little/no value added: there's no native type conversion, you probably need your own help, you are doing your own larger syntax, and your proc is CLI-only. Even parseopt* probably provides no real value add in this case.

All that said, I do provide pretty arbitrary ways to do things with parseOnly. See test/ParseOnly.nim for example usage. You can see, for example, how to access the .message field in the if clHelpOnly in fooParse: clause. That example does not actually dispatch, but you could just add another dispatchFoo(parseOnly=false) (or simply no argument at all) to do that.

lordvlad commented 5 years ago

Thanks for the quick answer. Is there maybe a better way to support positional arguments together with non-optional arguments? What I'd love to have is: configure(key, value="") and call it simply with my_cli config key got get some configuration and my_cli config key value to set some configuration

c-blake commented 5 years ago

Unless/until you have more params that aren't just generic string, you are probably better off just directly parsing os.commandLineParams(). You basically have that parser written already. Seems like you may want the user to be required to type a "config" argument.

c-blake commented 5 years ago

For example, if you put the below in a file called my_cli.nim you get what you want (obviously needing to flesh out (get|set)_config and the help message):

import os, sets

proc get_config(k: string) = echo "get ", k
proc set_config(k, v: string) = echo "set ", k, " = ", v

proc usage(extra="", status=0) =
  echo "help: ", extra
  quit(status)

const helps = [ "help", "--help", "-h" ].toSet
let args = commandLineParams()
if args.len < 1 or args[0] in helps: usage("", 0)
if args[0] != "config": usage("bad command", 1)
elif args.len == 1: usage("too few arguments", 1)
if   args.len == 2: get_config(args[1])
elif args.len == 3: set_config(args[1], args[2])
else: usage("too many arguments", 1)

If you were writing a module that had an import-able configure usable by Nim code instead of the CLI, or add params, or so on then you might be able to use cligen.dispatchMulti with a cmdName of "config" (or just name the proc config in the first place). Really it would just take seq[string], too, though. If empty string is an invalid "set" value then you could probably do something, but I don't see cligen as helping you very much in this use case.

cligen (is not about/was never about) trying to create any conceivable command syntax..Only a couple convenient/quasi-standard syntaxes. There was some discussion here https://github.com/c-blake/cligen/issues/62 about a new way to set positional that might potentially help. That work is not done yet, it might still not be exactly what you would want, and I have no personal need for this feature. If you wanted to work on a pull request, I'm not anti-inclusion of the feature, though.

lordvlad commented 5 years ago

thanks for the detailed answer

lordvlad commented 5 years ago

just found the mention of positionals in your TODO.md. Is this planned for the near future?

c-blake commented 5 years ago

That TODO item is not something I plan on working on as I don't have a need for that feature, as mentioned, but I'm open to someone else having a try and doing a PR or something.