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

initFromCL does not work with nesting #122

Closed timotheecour closed 4 years ago

timotheecour commented 4 years ago

initFromCL doesn't work with nesting; it should support recursively nested data, eg:

type Bar* = ref object
  host*: string
  port*: int

type Data* = ref object
  action*: string
  bar*: Bar

let dataDefault* = Data()

proc main(data: Data) = discard

when isMainModule:
  import pkg/cligen
  var app = initFromCL(dataDefault)
  app.main()

gives

cligen.nim(408, 29) ``tabId`.add(argHelp(`defVal`, `apId`) & descr)` Error: type mismatch: got <Bar, ArgcvtParams>

desired usage

./main -a:foo --bar.host:myhost

alternative

alternative would be to provide a json interface

c-blake commented 4 years ago

This isn't so much an initFromCL issue as much as a parseopt3/whole system issue. What you are proposing is really a new CL syntax/syntax extension where every option key now lives in some hierarchical namespace. It sounds complex, conceivably not popular, and probably not very "obvious". It would probably also make help table formatting harder to not be ugly.

For your specific use case, it would seem much simpler to define an argParse and argHelp for Bar, defaulting any unset fields. A more custom format would be friendlier than JSON. In this case the custom format the screams out would be like ./main --bar=myhost:80. The argParse/argHelp for that should be really easy to write.

I think more custom formats would be friendlier most of the time..not just in this example. More general JSON or even Nim's own object constructor syntax thing might appeal to laziness/generality, but then the CLI user has to learn/know another syntax. It may only even be workable when the CLI author is the only CLI user or a programmer who already knows the various languages in play. All those built-in arg(Parse|Help) we have now are for things like strings/enum names, numbers with near universally similar/identical syntax. Our advanced syntax is already probably right at/just beyond the limit of expanded CLI syntax.

Both hierarchical option key namespaces and automatic object initialization/printing feel like they are pushing the envelope too far in terms of keeping things easy & understandable to all concerned parties/user roles. Even if implementation effort were zero -- which it is not -- the "cons" likely outweight the "pros". In short, maybe a CLI author should be forced to construct a simple initializer/print syntax for object-valued keys.."for the good of the system".

If there are types in Nim's stdlib you want to submit PR's for for argcvt/argParse/argHelp, I am all ears. My own time to do such is limited.

c-blake commented 4 years ago

I should also have mentioned another thought relevant to evaluating this idea that I did not say "out loud" before. Were we to adopt a --hierarchy=atom style syntax then the CL user would have to repeat that hierarchy many times. The present --atom=CLauthor-defined-maybe-hierarchy-or-maybe-special-syntax-like-host:port is kind of fundamentally less repetitive (my verbose designator notwithstanding :-) ).

Yes, that could be addressed with yet more syntax on the LHS of the --key=value, such as some kind of grouping construct. Even the naturalness of "key,value", though, reveals that I (and probably most people) think of this as a "simple key, complex value" situation. So, there is a cognitive disconnect in that the --hierarchy=atom style syntax does not match the conceptual semantics.

Another consideration against --hierarchy=atom is how closely the object initialization from CL vs parameter list initialization parallel both each other and Nim-invocation named parameters. Currently, they are all basically the same/very similar with the top level object namespace matching the parameter list. That --hierarchy=atom idea would only make sense for objects, though. This is perhaps simply more expansion on the non-obvious aspect of it.

If you wanted to try some kind of JSON initializer/printer that would be ok, as long as it A) did not interfere with existing argParse/argHelp like for int, float, enum, etc and B) was itself easy to override by CL authors wanting other syntax, say <host>:<port> as in URIs. The bulk of the work is probably already done in the json module. I don't think cligen proper needs any real change for that. If you can achieve these twin objectives I am happy to include it in cligen/argcvt or otherwise such as include for people. I'm not against the generality or the laziness. I just don't want to sacrifice simple/easy to grok for the sake of general.