GithubHelp home page GithubHelp logo

c-blake / cligen Goto Github PK

View Code? Open in Web Editor NEW
492.0 492.0 23.0 6.86 MB

Nim library to infer/generate command-line-interfaces / option / argument parsing; Docs at

Home Page: https://c-blake.github.io/cligen/

License: ISC License

Nim 98.75% Shell 0.65% Makefile 0.38% sed 0.22%

cligen's Introduction

Unless otherwise specified, this is all Nim code.

Some driving libraries & tools:

  • cligen - library to infer/generate command-line-interfaces / option / argument parsing; Also has some IO/system interface utility modules to have no dependencies
  • adix - A Data structures for Indexing lib with sketches
  • nio - Low Overhead Numerical/Native IO lib & tools organized around the idea of compact file extensions as schemas
  • bu - Over 50 (mostly cligen) CLI utilities covering space management, file typology & times, benchmarking, pipeline/data formatting/calculation, sysadmin, tty tools, Nim package release, parallel & queuing shell tools, various front ends for adix things, random sampling & MORE

Adding some more color to your life:

  • lc - A post-modern, "multi-dimensional", colorful, configurable, abbreviating, extensible ls/file lister
  • procs - Colorful-like lc-Unix process & system query & formatting library & multi-command CLI
  • hldiff - A port of Python difflib to compute diffs and (re)highlight diff output intraline

Adding some math to your life:

  • spfun - Special Functions Of Stats / Physics / Etc.
  • fitl - Self-contained fit of linear models with regression diagnostics & various auto-regularization

Helping you handle graphs/words/documents:

  • gralg - Classic algorithms on graphs (the linked structure, not plots)
  • thes - a library & CLI interface to Moby Thesaurus that saves the DB in a packed binary format
  • suggest - A fast implementation of Mor & Fraenkel 1982's spell checking algo (co-opted/re-discovered by Wolfe Garbe) with a custom memory-mapped data file format/DB and some perf analysis
  • ndup - Near-Duplicate File Detection Code

Miscellaneous:

  • kslog - Lightweight Linux specific replacement for syslogd-ng in ~130 lines
  • cron - Very lightweight replacement for crond/tab in ~60 lines of Nim
  • nimp - a Nim package manager I use (but doubt anyone else will) in ~320 self-contained lines
  • ftab - Native File-Based Hash Table library like DBM; SQLite can be overkill/slow

An experiment:

  • nimsearch - Try to explain some elementary ideas of search engine impl via successive diffs / patches

Some C packages:

  • batch - A Linux Kernel module to create a new sys_batch system call that runs packaged mini-programs with 1 kernel crossing
  • bst - Well Factored, Non-Recursive, General & Generic BSTs in ANSI C - unbalanced, AVL, red-black, Lk-weight & splay

cligen's People

Contributors

avdn avatar c-blake avatar clyybber avatar cooldome avatar cybertailor avatar enthus1ast avatar genotrance avatar hirakida avatar hoijui avatar jiro4989 avatar kaushalmodi avatar oskaritimperi avatar pysan3 avatar ringabout avatar sirnickolas avatar solitudesf avatar sspkrolik avatar timotheecour avatar vindaar avatar zoomrmc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cligen's Issues

dispatchGen should have an option to forward the result "as is"

in the same spirit as #45 (ie, having a more flexible way to integrate dispatch in a program), I would like to have an option to forward the result "as is":
while this doesn't make sense for dispatch (which must have command line semantitcs, ie int return code), it does make a lot of sense for dispatchGen which can apply user defined logic on return value.

right now the only options are: echoResult=true|false which isnt' as flexible

proposal

macro dispatchGen(pro: typed, ..., forwardResult = false)
when forwardResult = true, `echoResult` argument is ignored, and the return type of `dispatchFoobar` shall be that of `foobar`  for dispatchGen(foobar, ... , forwardResult=true)

allow arguments with a single letter: `proc foobar(f=1)`

proc foobar(f=1)
should be allowed and generate:
-f=, --f=

instead of: Error: duplicate case label

  • this avoids having to transform function signatures just to use cligen ; makes it easy for debugging
  • it's essential when we don't have control over function signature, eg if we want to dispatch(foobar) for some foobar in some imported

Error in dispatchMulti

nim --version
Nim Compiler Version 0.16.0 (2017-01-12) [Linux: amd64]
Copyright (c) 2006-2017 by Andreas Rumpf

The exact code from the example:

proc foo(myMandatory: int, mynums: seq[int], foo=1, verb=false) =
  ##Some API call
proc bar(myHiHo: int, myfloats: seq[float], verb=false) =
  ##Some other API call
when isMainModule:
  import cligen; dispatchMulti([foo], [bar])

Compiling:

nim c foo.nim

Error:

foo.nim(6, 31) Error: type mismatch: got (array[0..0, proc (myMandatory: int, mynums: seq[int], foo: int, verb: bool){.noSideEffect, gcsafe, locks: 0.}], array[0..0, proc (myHiHo: int, myfloats: seq[float], verb: bool){.noSideEffect, gcsafe, locks: 0.}])
but expected one of:
macro dispatchMulti(cmdName = "?"; procBrackets: varargs[untyped]): untyped

Adjusting code:

proc foo(myMandatory: int, mynums: seq[int], foo=1, verb=false) =
  ##Some API call
proc bar(myHiHo: int, myfloats: seq[float], verb=false) =
  ##Some other API call
when isMainModule:
  import cligen; dispatchMulti(foo, bar)

Similar error:

foo.nim(6, 31) Error: type mismatch: got (proc (myMandatory: int, mynums: seq[int], foo: int, verb: bool){.noSideEffect, gcsafe, locks: 0.}, proc (myHiHo: int, myfloats: seq[float], verb: bool){.noSideEffect, gcsafe, locks: 0.})
but expected one of:
macro dispatchMulti(cmdName = "?"; procBrackets: varargs[untyped]): untyped

help doesn't check keys are valid

proc main(foo = "myfoo") = discard
dispatch(main, help = {"foo2": "help msg"})

this should give compiler error (foo2 instead of foo) but doesn't

(btw, haven't answered yet to your other answers, it's on my TODO list, thanks for the detailed answers so far!)

This repository has no version tags, making it impossible to manage as a dependency.

There doesn't seem to be any version tags on the repo, and the version field was last updated two years.

This is an issue because it means that when this project is a dependency it is impossible to request any version except the latest one. And right now the latest version of this project fails to compile with the current stable version of Nim. So at the moment any project that depends on cligen via nimble is broken unless you are running on the Nim devel branch.

For example, the following short example is very difficult to compile on nim 0.17.3:

test.nimble

# Package

version       = "0.1.0"
author        = "Jonathan Bernard"
description   = "Example of difficulty with cligen on Nim 0.17.3"
license       = "MIT"
bin           = @["test"]

# Dependencies

requires "nim >= 0.17.3"

# The following depends transitively on cligen
requires "https://github.com/yglukhov/nim-jwt"

test.nim

echo "Hello World!"

Trying to build the project:

$ nim --version
Nim Compiler Version 0.17.3 (2017-11-15) [Linux: amd64]
Copyright (c) 2006-2017 by Andreas Rumpf

git hash: 25e825df3ad1832c5795eb06369b8fe660a4d02d
active boot switches: -d:release

$ nimble --version
nimble v0.8.8 compiled at 2017-11-15 20:45:37
git hash: f85219872421b47e6b4a8bcbc7c317c9d1304e2d

$ nimble build
Verifying dependencies for [email protected]
 Installing https://github.com/yglukhov/nim-jwt@any version
Downloading https://github.com/yglukhov/nim-jwt using git
   Warning: Package 'jwt' has an incorrect structure. It should contain a single directory hierarchy for source files, named 'jwt', but file 'jose.nim' is in a directory named 'private' instead. This will be an error in the future.
      Hint: If 'private' contains source files for building 'jwt', rename it to 'jwt'. Otherwise, prevent its installation by adding `skipDirs = @["private"]` to the .nimble file.
  Verifying dependencies for [email protected]
 Installing https://github.com/yglukhov/linktools@any version
Downloading https://github.com/yglukhov/linktools using git
  Verifying dependencies for [email protected]
 Installing cligen@any version
Downloading https://github.com/c-blake/cligen.git using git
   Warning: Package 'cligen' has an incorrect structure. The top level of the package source directory should contain at most one module, named 'cligen.nim', but a file named 'parseopt3.nim' was found. This will be an error in the future.
      Hint: If this is the primary source file in the package, rename it to 'cligen.nim'. If it's a source file required by the main module, or if it is one of several modules exposed by 'cligen', then move it into a 'cligen/' subdirectory. If it's a test file or otherwise not required to build the the package 'cligen.nim', prevent its installation by adding `skipFiles = @["parseopt3.nim"]` to the .nimble file. See https://github.com/nim-lang/nimble#libraries for more info.
  Verifying dependencies for [email protected]
 Installing [email protected]
   Success: cligen installed successfully.
 Installing [email protected]
   Building linktools/linktools using c backend
       Tip: 52 messages have been suppressed, use --verbose to show them.
     Error: Build failed for package: linktools
        ... Details:
        ... Execution failed with exit code 1
        ... Command: "/home/jdb/programs/nim/bin/nim" c --noBabelPath -d:release --path:"/tmp/nimble_25070/githubcom_cblakecligengit"  -o:"/tmp/nimble_25070/githubcom_yglukhovlinktools/linktools" "/tmp/nimble_25070/githubcom_yglukhovlinktools/linktools.nim"
        ... Output: Hint: used config file '/home/jdb/programs/nim/config/nim.cfg' [Conf]
        ... Hint: system [Processing]
        ... Hint: linktools [Processing]
        ... Hint: strutils [Processing]
        ... Hint: parseutils [Processing]
        ... Hint: math [Processing]
        ... Hint: algorithm [Processing]
        ... Hint: osproc [Processing]
        ... Hint: os [Processing]
        ... Hint: times [Processing]
        ... Hint: posix [Processing]
        ... Hint: ospaths [Processing]
        ... Hint: strtabs [Processing]
        ... Hint: hashes [Processing]
        ... Hint: streams [Processing]
        ... Hint: cpuinfo [Processing]
        ... Hint: linux [Processing]
        ... Hint: cligen [Processing]
        ... Hint: macros [Processing]
        ... Hint: tables [Processing]
        ... Hint: parseopt3 [Processing]
        ... /tmp/nimble_25070/githubcom_cblakecligengit/cligen.nim(176, 15) Error: undeclared identifier: 'toNimIdent'

Show errors for ambigious symbol

Example:

import cligen, options

func get() = discard

dispatch get

This will fail due to ambiguous symbol get provided by both options and the current module, and the error message is certainly unhelpful:

nimble/pkgs/cligen-0.9.17/cligen.nim(215) dispatchGen
test.nim(5, 10) template/generic instantiation of `dispatch` from here
nimble/pkgs/cligen-0.9.17/cligen.nim(215, 19) Error: node is not a symbol

Provide a pluggable interface for alternative configuration value sources (files, env vars)

A nice feature of command lines is if you can stick the same runtime option values in config files or pick them out from environment variables.

Since cligen builds a mapping of name-to-value, would be interesting if there was support for building up such chains of option-sources from one source of truth about what the valid options are - specially interesting if #30 happens and options can be gathered in objects that are a little bit more complex.

This API would need to give the parser names and types to work with as well as a way to feed back the values read. Further, it would have to support prioritizing, so that something on command line overrides config file.

[TODO] feature: allow passing as nim arguments`--nimargs='32.0, foo: 2, bar:"hi", x=@[1,2]'`

I had actually implemented something like that in D, I think it would make a lot of sense for cligen.

the Idea is to have as an option a way to call your nim function from cmd line in the same way as you'd call from nim, eg:

void fun(a: double, foo: int, bar: string, x: seq[int]):auto=...
./app --nimargs='32.0, foo: 2, bar:"hi", x=@[1,2]'
#same as
./app --a=32.0 --foo=2 --bar:hi ...

this makes it easy to interchangeably call your program either from cmd line or from nim / inim

weird ` ,SV[string]` in help msg

toast -h
Usage:
  main [required&optional-params]
  Options(opt-arg sep :|=|spc):
  -h, --help                                  write this help to stdout
  -m=, --mode=         string       "cpp"     language; see CompileMode
  -p, --past           bool         false     print AST output
  --pnim               bool         false     run preprocessor on header
  --pretty             bool         true      set pretty
  --preprocess         bool         false     print Nim output
  -d=, --defines=      ,SV[string]  EMPTY     set defines
  -i=, --includeDirs=  ,SV[string]  EMPTY     include directory to pass to preprocessor
  -s=, --source=       string       REQUIRED  C/C++ source/header
  • why the , in ,SV[string] ?
  • what does SV stand for? (it's not in README)

allow passing context variables

motivation

see use case in #48 (difficulty 3)

proposal

myapp.nim:

proc main(pass: TCmdLinePass, conf: ConfigRef, query = "", project = "", rebuild = false) = # I want to wrap this by cligen
dispatchGen(main, skip=@[pass, conf])
# generates:
proc dispatchMain(pass: TCmdLinePass, conf: ConfigRef, args: seq[string]) = ... # calls main(pass, conf, ...)

# this (see nimfind.nim) can now use dispatchMain:
proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
  args = cmdToArgs(cmd) # TODO (out of scope for this discussion)
  dispatchMain(pass, conf, args)

proposal 2 (preferred)

Note: this is very related to #30

introduce a new proc in cligen.nim:

proc dispatchParse[T](params: T, args: seq[string]) =
  # parses cmd line arguments into fields of params

(there could be some partial code reuse so that dispatchGen and dispatchParse could reuse some code for the seq[string] param conversions)

somehow have an option for dispatchGen to just do parameter conversion given fields of an object, eg:

type Params = ref object
  query: string
  project: string
  rebuild: bool
proc initParams(): Params = ...
proc main2(pass: TCmdLinePass, conf: ConfigRef, params: Params) = ...

# this (see nimfind.nim) can now use dispatchMain:
proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
  args = cmdToArgs(cmd) # TODO (out of scope for this discussion)
  var params = initParams()
  dispatchParse(params, args)
  main2(pass, conf, params)

Unable to create library when using cligen

nim compile -d:Release --app:lib --header src/cli.glm

When I run the above I get the following error:

Hint: ast [Processing]
Hint: cligen [Processing]
Hint: parseopt3 [Processing]
../../../.nimble/pkgs/cligen-#head/cligen/parseopt3.nim(92, 44) Error: undeclared identifier: 'commandLineParams'

Default dispatch in dispatchMulti

A default "dispatch-route" would be nice in a dispatchMulti case when no dispatch is explicitly named in the arguments.

mb. something like this:
dispatchMulti([foo, default=true], [bar])

Support enums

Hey, it would be neat if dispatch would support procs with enums in their arguments and limit their input accordingly when constructing the cli.

Currently this does not seem supported.

Reorganize the project as a nimble package?

Hello,

Can you please reorganize the project contents so that the below Just WorksTM:

git clone https://github.com/c-blake/cligen
cd cligen
nimble build

At the moment, doing so gives:

   Warning: Package 'cligen' has an incorrect structure. The top level of the package source directory should contain at most one module, named 'cligen.nim', but a file named 'argcvt.nim' was found. This will be an error in the future.
      Hint: If this is the primary source file in the package, rename it to 'cligen.nim'. If it's a source file required by the main module, or if it is one of several modules exposed by 'cligen', then move it into a 'cligen/' subdirectory. If it's a test file or otherwise not required to build the the package 'cligen.nim', prevent its installation by adding `skipFiles = @["argcvt.nim"]` to the .nimble file. See https://github.com/nim-lang/nimble#libraries for more info.
  Verifying dependencies for [email protected]
       Tip: 2 messages have been suppressed, use --verbose to show them.
     Error: Nothing to build. Did you specify a module to build using the `bin` key in your .nimble file?

Nimble Project Structure

Basic example doesn't work on devel toolchain

proc foobar(foo=1, bar=2.0, baz="hi", verb=false, paths: seq[string]): int =
  ##Some existing API call
  result = 1          # Of course, real code would have real logic here

when isMainModule: import cligen; dispatch(foobar)    # Whoa...Just one line??
๏ผ โ€ฆ/container_cmd ๎‚ฐ ๏„“  ๏„ฆ master ๏™ ๎‚ฐ nim c -r "/home/ahmed/wspace/container_cmd/src/container_cmd.nim"
Hint: used config file '/home/ahmed/.choosenim/toolchains/nim-#devel/config/nim.cfg' [Conf]
Hint: system [Processing]
Hint: container_cmd [Processing]
Hint: cligen [Processing]
Hint: macros [Processing]
Hint: tables [Processing]
Hint: hashes [Processing]
Hint: strutils [Processing]
Hint: parseutils [Processing]
Hint: math [Processing]
Hint: bitops [Processing]
Hint: algorithm [Processing]
Hint: unicode [Processing]
Hint: parseopt3 [Processing]
Hint: os [Processing]
Hint: times [Processing]
Hint: options [Processing]
Hint: typetraits [Processing]
Hint: strformat [Processing]
Hint: posix [Processing]
Hint: ospaths [Processing]
/home/ahmed/.nimble/pkgs/cligen-0.9.15/cligen/parseopt3.nim(94, 46) Error: type mismatch: got <nil> but expected 'seq[string]'

Nested procs/dispatches

As title states, don't think it's possible yet.

Something like:

myapp someproc someotherprocfoo --verbose
myapp someproc someotherprocbar --quiet

myapp someproc
usage: myapp someproc [someotherprocfoo | someotherprocbar]

Yay/Nay? - Can you give an inkling how much work this would be?

Thanks for your input.

subcommands command grouping?

How can I achieve

app container list
app container create -n aname -i imagename

?
or that needs manual work to have proc container with positional arguments? and somehow manually parse the rest of the arguments?

dispathAs(foobar, foobarMain) => creates `proc foobarMain(args: seq[string]): int`

/cc @c-blake

I'd like to use cligen in a more flexible way as follows:

proc foobar(foo=1, bar=2.0, baz="hi", verb=false, paths: seq[string]): int =
  ...

dispathAs(foobar, foobarMain)
# creates `proc foobarMain*(args: seq[string]): int` ; this can be called eg via: foobarMain(["-f:3", "--baz:mybaz"])

it adds flexibility to integrate in more complex libraries, eg when args are provided from another source (could be forwarded from post processed command line or from any other source)

Hopefully this is a simple change

argRet shouldn't use return

Currently argRet is implemented like this:

template argRet*(code: int, msg: string) =
  ## argRet is a simple space-saving template to write msg and return a code.
  stderr.write(msg)                         # if code==0 send to stdout?
  return code

This means that it inserts a return in the caller which sometimes break unexpectedly:

Example

import cligen, argcvt # argcvt is part of cligen
import strutils

type
  License = enum
    MIT, Apache2, GPLv2, GPLv3

  Licenses = set[License]

# Add Licenses support to cligen
template argParse(dst: Licenses, key: string, val: string, help: string) =
  let args = val.split(',')
  for input_license in args:
    for supported_license in low(License)..high(License):
      if cmpIgnoreStyle(input_license, $supported_license) == 0:
        incl(dst, supported_license)
      else:
        argRet(1, "Wrong input license(s) for param \"$1\"\n$2" %
               [key, help])

when isMainModule:
  var test: Licenses
  argParse(test, "key", "MIT,GPLv2", "help")

Error is: Error: 'return' not allowed here

Maybe implement it like this?

template argRet*(code: int, msg: string) =
  ## argRet is a simple space-saving template to write msg and return a code.
  stderr.write(msg)                         # if code==0 send to stdout?
  code

Or as a proc?

proc argRet*(code: int, msg: string): int =
  ## argRet is a simple space-saving template to write msg and return a code.
  stderr.write(msg)                         # if code==0 send to stdout?
  return code

option to dispatch from object's fields => leads to better API's and more DRY code

currently code using cligen is not as DRY as it could be, eg:

type App = ref object
    nim: string
    srcFile: string
    showHeader: bool
    other: string

proc main(nim="nim", srcFile = "", showHeader = true, other = nil) =
    app.new()
    app.nim=nim
    app.srcFile=srcFile
    app.showHeader=showHeader
    if other == nil:
      app.other = nim & " FOOBAR"
    else:
      app.other=other
   
   # rest of code...
   echo app.repr

when isMainModule:
    import cligen
    dispatch(main, help = {
            "nim": "compiler to use",
            "srcFile": "script to run",
            "showHeader": "show informative compiler info",
             "other": "defaults to smthg inferred from other args",
        })

I'd like to be able to write instead the following, while keeping the same generated cmd line:

type App = ref object
  nim: string ## compiler to use
  srcFile: string ## script to run
  # non-doc comment: this won't appear in `help`
  showHeader: bool ## show informative compiler info
  other: string ## defaults to smthg inferred from other args

proc newApp(): auto=
  result = App(nim:"foo", srcFile:"bar", showHeader: false)
  result.other = result.nim & " FOOBAR"
  
proc main(app = newApp()) =
  echo app.repr

when isMainModule:
  import cligen
  dispatch(main) # help inferred from fields' documentation comments

advantages:

  • the documentation is where it should be, in the field definition (also, nim doc could see those I believe); NOTE: only documentation comments would be exposed in help
  • with lots of fields, passing args through an object is cleaner than passing each field individually (similar rationale as haltcase/glob@3fbdb18)
    especially when main needs to be called as a library from another function: better to pass a single object/ref object than pass each field individually (ie, encapsulation)

NOTE:

  • feature should work with either object or ref object

Follow nep1

Nim devel now has a --nep1:on switch which checks the code if it follows NEP1: https://nim-lang.org/docs/nep1.html.

So while running one of my toy Nim projects with --nep1:on, it also scanned through cligen as they project is using it.

This is what it output:

/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'pkgParamDispatch' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'pkgParamDispatch' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'pkgParamDefault' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'pkgParamDefault' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'revParamDispatch' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'revParamDispatch' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'revParamDefault' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'revParamDefault' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'gitSkipParamDispatch' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'gitSkipParamDispatch' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'gitSkipParamDefault' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'gitSkipParamDefault' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'waitSkipParamDispatch' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'waitSkipParamDispatch' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'waitSkipParamDefault' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'waitSkipParamDefault' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'installSkipParamDispatch' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'installSkipParamDispatch' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'installSkipParamDefault' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'installSkipParamDefault' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'keepParamDispatch' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'keepParamDispatch' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'keepParamDefault' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'keepParamDefault' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'debugParamDispatch' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'debugParamDispatch' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'debugParamDefault' [Name]
/home/kmodi/.nimble/pkgs/cligen-0.9.11/cligen.nim(328, 20) Hint: name should be: 'debugParamDefault' [Name]

Would you like to refactor these?

discussion: how to convert parseopt style to cligen

motivation

let's take this for example:
https://github.com/nim-lang/Nim/blob/4bd9f32f335b43d1cdcfbbd99cbe91bb478a400c/tools/nimfind.nim#L182

I'm curious what'd be the best way to convert this to cligen (eg to simplify code while retaining as much as possible backward compatiblity)

(another more ambitious example would be converting Nim's cmd line to cligen while retaining backward compatibility)

proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
  var p = parseopt.initOptParser(cmd)
  while true:
    parseopt.next(p)
    case p.kind
    of cmdEnd: break
    of cmdLongoption, cmdShortOption:
      case p.key.normalize
      of "help", "h":
        stdout.writeline(Usage)
        quit()
      of "project":
        conf.projectName = p.val
      of "rebuild":
        incl conf.globalOptions, optForceFullMake
      else: processSwitch(pass, p, conf)
    of cmdArgument:
      let info = p.key.split(':')
      if info.len == 3:
        let (dir, file, ext) = info[0].splitFile()
        conf.projectName = findProjectNimFile(conf, dir)
        if conf.projectName.len == 0: conf.projectName = info[0]
        try:
          conf.m.trackPos = newLineInfo(conf, AbsoluteFile info[0],
                                        parseInt(info[1]), parseInt(info[2])-1)
        except ValueError:
          quit "invalid command line"
      else:
        quit "invalid command line"

difficulty 1

parseopt distinguishes whether a flag was passed or not; with cligen, there's ambiguity in case provided flag is equal to default value
=> see #49

difficulty 2

this uses this:

processSwitch(pass, p, conf)

to forward remaining flags not recognized by nimfind's processCmdLine to compiler/commands.nim 's processSwitch
I'm interested in a solution that will convert processCmdLine to cligen style dispatch, but will, as above, forward unhandled arguments to processSwitch (to facilitate integration and avoid having to convert that too to cligen)

difficulty 3 (biggest challenge)

extra (context) params are given (pass and conf)
I was thinking of using nested functions but that runs into issues because cligen generates imports which must be at toplevel:

proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
  proc main(query = "", project = "", rebuild = false) = # I want to wrap this by cligen
     ...

  # this can't work (would generate `import` inside a scope)
  dispatchGen(main, help = Usage)

another option (ugly) is using global variables (pass, conf) but that can't always work reliably

=> see #50

Cannot use `delete` as the function name

If I create a package using nimble init called commandlinenim and save the following in ./src/commandlinenim.nim,

import strformat

proc delete(file: string): int =
    echo fmt"Deleting {file}"

when isMainModule:
    import cligen
    dispatch(delete)

and change the nimble file to point to cligen#head, and run the following from the root directory,

nimble build

I get the following error

  Verifying dependencies for [email protected]
      Info: Dependency on cligen@#head already satisfied
  Verifying dependencies for cligen@#head
   Building commandlinenim/commandlinenim using c backend
       Tip: 6 messages have been suppressed, use --verbose to show them.
     Error: nimble.nim(1106)         nimble
        ... nimble.nim(1064)         doAction
        ... nimble.nim(510)          build
        ... nimble.nim(245)          buildFromDir
        ... Build failed for package: commandlinenim
        ... Details:
        ... nimble.nim(1106)         nimble
        ... nimble.nim(1064)         doAction
        ... nimble.nim(510)          build
        ... nimble.nim(237)          buildFromDir
        ... tools.nim(37)            doCmd
        ... Execution failed with exit code 1
        ... Command: "/Users/$USER/.nimble/bin/nim" c --noBabelPath --path:"/Users/$USER/.nimble/pkgs/cligen-#head"  -o:"/Users/$USER/GitRepos/commandlinenim/commandlinenim" "/Users/$USER/GitRepos/commandlinenim/src/commandlinenim.nim"
        ... Output: Hint: used config file '/Users/$USER/.choosenim/toolchains/nim-0.19.0/config/nim.cfg' [Conf]
        ... Hint: system [Processing]
        ... Hint: commandlinenim [Processing]
        ... Hint: strformat [Processing]
        ... Hint: macros [Processing]
        ... Hint: parseutils [Processing]
        ... Hint: unicode [Processing]
        ... Hint: strutils [Processing]
        ... Hint: math [Processing]
        ... Hint: bitops [Processing]
        ... Hint: algorithm [Processing]
        ... Hint: cligen [Processing]
        ... Hint: tables [Processing]
        ... Hint: hashes [Processing]
        ... Hint: parseopt3 [Processing]
        ... Hint: os [Processing]
        ... Hint: times [Processing]
        ... Hint: options [Processing]
        ... Hint: typetraits [Processing]
        ... Hint: posix [Processing]
        ... Hint: ospaths [Processing]
        ... Hint: argcvt [Processing]
        ... Hint: sets [Processing]
        ... Hint: textUt [Processing]
        ... Hint: terminal [Processing]
        ... Hint: colors [Processing]
        ... Hint: termios [Processing]
        ... commandlinenim.nim(8, 13) template/generic instantiation from here
        ... ../../../.choosenim/toolchains/nim-0.19.0/lib/system.nim(1541, 28) Error: undeclared identifier: 'T'

It appears that I cannot use delete as a function name.

[Feature request] generate cli parser from an object definition

While playing with cligen I thought that another convenient feature would be generating the parser from an object, or from complex objects, accepted as args from the main function. Example:

type CliOptions = object # or ref object. or tuple...
  someArg1: string
  someArg2: bool

proc mainProc(options: CliOptions): int = discard
myTool --someArg1=hi --someArg2

This way it is much easier to pass cli options from within mainProc to other procs. The idea is pretty raw and needs more thought regarding positional args, etc.

Build breaks on latest devel

Hello,

I have been tracking the devel branch. Today I found that projects using cligen don't build now, probably due to the recent "strings cannot be nil" backward incompatible change..

Error

Downloading https://github.com/kaushalmodi/nistow using git
  Verifying dependencies for [email protected]
      Info: Dependency on cligen@>= 0.9.11 already satisfied
  Verifying dependencies for [email protected]
 Installing [email protected]
   Building nistow/nistow using c backend
       Tip: 6 messages have been suppressed, use --verbose to show them.
     Error: Build failed for package: nistow
        ... Details:
        ... Execution failed with exit code 1
        ... Command: "/home/kmodi/stowed/bin/../../stow/pkgs/nim/devel/bin/nim" c --noBabelPath -d:release --path:"/home/kmodi/.nimble/pkgs/cligen-0.9.15"  -o:"/tmp/nimble_5954/githubcom_kaushalmodinistow/nistow" "/tmp/nimble_5954/githubcom_kaushalmodinistow/src/nistow.nim"
        ... Output: Hint: used config file '/home/kmodi/stow/pkgs/nim/devel/config/nim.cfg' [Conf]
        ... Hint: system [Processing]
        ... Hint: nistow [Processing]
        ... Hint: os [Processing]
        ... Hint: strutils [Processing]
        ... Hint: parseutils [Processing]
        ... Hint: math [Processing]
        ... Hint: bitops [Processing]
        ... Hint: algorithm [Processing]
        ... Hint: unicode [Processing]
        ... Hint: times [Processing]
        ... Hint: options [Processing]
        ... Hint: typetraits [Processing]
        ... Hint: strformat [Processing]
        ... Hint: macros [Processing]
        ... Hint: posix [Processing]
        ... Hint: ospaths [Processing]
        ... Hint: cligen [Processing]
        ... Hint: tables [Processing]
        ... Hint: hashes [Processing]
        ... Hint: parseopt3 [Processing]
        ... /home/kmodi/.nimble/pkgs/cligen-0.9.15/cligen/parseopt3.nim(94, 46) Error: type mismatch: got <nil> but expected 'seq[string]'

Recipe

On the latest Nim devel:

nimble install https://github.com/kaushalmodi/nistow

Is there a way to suppress GcUnsafe warnings?

E.g.

main.nim(242, 18) template/generic instantiation from here
cligen/cligen.nim(283, 22) Warning: not GC-safe: 'main(:env.MyArgcligenDispatcher294045, ...)' [GcUnsafe]
main.nim(242, 19) Warning: not GC-safe: 'dispatchmain(commandLineParams(), "", "${prelude}$command $args\x0A$doc  Options(opt-arg sep :|=|spc):\x0A$options$sep",
             "", "")' [GcUnsafe]

Maybe a pragma could suppress that? I hate to have any warnings in my compilation.

feature: `--foo+=mypath` to append to `foo`

this is a common use case:
supporting appending to an array as opposed to resetting.

example 1: append to a seq[string]

proc foobar(imports = @[".", ".."], libs = @["dl", "pthread"], paths: seq[string]) = discard

The following rules are very intuitive and add lots of flexibility, especially for letting users choose how to override default configs:

./app
imports=@[".", ".."]

./app --imports="my other lib"
imports=@["my other lib"]

./app --imports= rest of args #notice space after `=`
imports=@[""]

./app --imports+="baz"
imports=@[".", "..", "baz"]

./app --imports+="baz1" --imports+="baz2"
imports=@[".", "..", "baz1", "baz2"]

./app --imports="baz1" --imports+="baz2"
imports=@["baz1", "baz2"]

./app --imports+= rest of args  #notice space after `=`
imports=@[".", "..", ""]

example 2: append to a string

same behavior as above, eg:

proc foobar(path = "foo:bar", paths: seq[string]) = discard

./app --path+=":baz"
imports="foo:bar:baz"

example 3: append to a seq[int]

same concept...

NOTE

&= would be more Nim-esque, but that is inconvenient for shells as & would have to be escaped.

Cannot use default value in dispatchMulti

If I have the following example:

proc say(greeting: string = "hello"): int =
    echo greeting
    return 0

when isMainModule:

    import cligen

    dispatchMulti([say])

I can run the cli as follows:

$ ./main 
Usage:  This is a multiple-dispatch cmd.  Usage is like
  ./main subcommand [subcommand-opts & args]
where subcommand syntaxes are as follows:

  say [optional-params]
    Options(opt-arg sep :|=|spc):
      -h, --help                         write this help to stdout
      -g=, --greeting=  string  "hello"  set greeting

However, when I call the subcommand:

$ ./main say
say does not expect non-option arguments
say [optional-params]
  Options(opt-arg sep :|=|spc):
  -h, --help                         write this help to stdout
  -g=, --greeting=  string  "hello"  set greeting

I now have to pass the option parameter in order to set a value.

$ ./main say --greeting "hi"
hi

This is a problem also for boolean flags:

proc say(pretty: bool = true): int =
    echo "hello"
    return 0

when isMainModule:

    import cligen

    dispatchMulti([say])

I'm unable to run the above without setting the flag.

allow distinguishing between argument not passed vs argument passed and equal to default

problem

proc main(foo = "") =
  # how do we tell whether foo was passed to cmd line in case the value passed is equal to default value, eg: `--foo=""` ?

for the cases where it matters, we need a way to distinguish it; for example for use case mentioned in #48

proposal 1

maybe there'd be a way to add magic to dispatchGen via witness boolean flags, eg:

proc main(foo = "", bool witnessFoo = false) =
  # now we can distinguish via `witnessFoo`

dispatchGen could do the necesary magic to make this all transparent; the corresponding valid args on cmd line would be:

--foo
--help
# not --witnessFoo which is not converted to a cmd line flag but instead used to distinguish the ambiguous case above

note: we can have multiple such witness arguments, as needed, eg:

proc main(foo1 = "", foo2 = 32, foo3 = 2.1, bool witnessFoo1 = false, bool witnessFoo3) = ...

proposal 2

use Option[T] arguments, eg:

import options
proc main(foo = none[string]())=
  if foo.isNone:
    # could specify a default here
    discard
  else:
    echo foo.get
main()
main(some("foobar"))

proposal 3 (seems best)

cligen exposes OptionDefault type so clients who want to distinguish a particular param can use it for that argument
cligen then handles these arguments appropriately, making these transparent

# in cligen.nim
type OptionDefault*[T]=object
  value*: T
  initialized*: bool

proc defVal*[T](a: T): OptionDefault[T] =
  result.value = a

proc setVal*[T](a: T): OptionDefault[T] =
  result.value = a
  result.initialized = true

# in myapp.nim
import cligen

proc main(foo = "".defVal, bar = 0.defVal) =
  echo (foo.value, foo.initialized)
  echo (bar.value, bar.initialized)

proc test()=
  # this shows that we've disambiguated `foo`:
  main() # called when we call ./myapp
  main(foo = setVal("")) # called when we call ./myapp --foo:""

when isMainModule:
  dispatch(main)
./myapp
./myapp --foo:""

How can I configure cligen to expect a mandatory non-option argument?

I have a single dispatch use case like this:

when isMainModule:
  import cligen
  dispatch(ntangle
           , version = ("version", "0.1.1"))

But I need a mandatory non-option argument for this CLI tool. I expect to run it as: ntangle FILENAME.

In future I plan to add more switches so that ntangle FILENAME --someSwitch would be a possible command too.

Can I have a mix of both non-option and option (or switch) args?

Examples: Parsing sets/flags and enums

I used cligen for nimbus-launch, a nim project skeleton generator. First of all, awesome work.

Here are some snippets that can be reused as example to parse the following types from the CLI.

type
  License* = enum
    MIT, Apachev2, GPLv2, GPLv3

  Licenses* = set[License]

  TravisConfig* = enum
    StatusDocker, Generic
template argParse*(dst: Licenses, key: string, val: string, help: string) =
  # Parse license input
  let args = val.split(',')
  dst = {}
  for input_license in args:
    var isValid: bool = false
    for supported_license in low(License)..high(License):       # Interesting read: "parseEnum is slow" https://forum.nim-lang.org/t/2949
      if cmpIgnoreStyle(input_license, $supported_license) == 0:
        incl(dst, supported_license)
        isValid = true
    if not isValid:
      argRet(1, "Wrong input license(s) for param \"$1\"\n$2, only MIT, Apachev2, GPLv2 and GPLv3 are supported." %
             [key, help])

template argHelp*(helpT: seq[array[0..3, string]], defVal: Licenses,
                  parNm: string, sh: string, parHelp: string) =
  helpT.add([ keys(parNm, sh), "Licenses", $defVal, parHelp ])


template argParse*(dst: TravisConfig, key: string, val: string, help: string) =
  # Parse TravisConfig input
  var isValid: bool = false
  for supported_config in low(TravisConfig)..high(TravisConfig):       # Interesting read: "parseEnum is slow" https://forum.nim-lang.org/t/2949
    if cmpIgnoreStyle(val, $supported_config) == 0:
      dst = supported_config
      isValid = true
  if not isValid:
    argRet(1, "Wrong input travis config for param \"$1\"\n$2, only StatusDocker and Generic are supported." %
            [key, help])

template argHelp*(helpT: seq[array[0..3, string]], defVal: TravisConfig,
                  parNm: string, sh: string, parHelp: string) =
  helpT.add([ keys(parNm, sh), "Travis: ", $defVal, parHelp ])

How to dispatch a subcommand using an alias name?

I tried

  import cligen

  dispatchMulti(
    [startZimHttpServer, cmdName="server"] # full sig: proc startZimHttpServer*(filename: string, port: uint16 = 8080) = 
  )

And when I call zim (binary name) it seems to understand that "server" is the alias, but when I call zim server it tells me that it wants zim startZimHttpServer which then works.

Feature request: Allow converting camelcase/snake-case proc arguments to standard Unix-style long options

Hello,

I am trying to use cligen to generate standard Unix-style long options like --foo-bar instead of --fooBar.

As cligen infers the long form switch names from the proc arguments, I cannot set the argument identifiers to something like git-skip or wait-skip (as I would like to get the long form options as --git-skip and --wait-skip). Instead I need to do GitSkip (ignore the capital G.. I did that just as a quick way to get -G as the short form switch).

Would it be possible to have a cligen configuration that auto-converts all camel/snake case proc args to kebab-case for the long-form switches?

Here is what the help of my little script looks like:

Usage:
  nbuild [required&optional-params]
NBuild: General purpose build script
  Options(opt-arg sep :|=|spc):
  -h, --help                                  print this help message
  -p=, --pkg=        string  REQUIRED         set pkg
  -r=, --rev=        string  "origin/master"  set rev
  -G, --GitSkip      bool    false            set GitSkip
  -W, --WaitSkip     bool    false            set WaitSkip
  -I, --InstallSkip  bool    false            set InstallSkip
  -k, --keep         bool    false            set keep
  -d, --debug        bool    false            set debug

Here is the full code.

[TODO] suggestions

  • use object instead of tons of params for dispatch, dispatchGen

would make code more readable, reusable, and more DRY

  • separate out long string constant into a const string
    also, it's a bit cleaner to separate out "${prelude}$command $args\n$doc Options(opt-arg sep :|=|spc):\n$options$sep", as a separate const instead of inlining this string:
const usage = ""${prelude}$command $args\n$doc  Options(opt-arg sep :|=|spc):\n$options$sep"
  • instead of:
echo "Usage:  This is a multiple-dispatch cmd.  Usage is like"
        echo "  $1 subcommand [subcommand-opts & args]" % [ `srcBase` ]
        echo "where subcommand syntaxes are as follows:\n"

use a single echo + fmt

echo fmt"...{srcBase} ... "
  • likewise with
"${prelude}$command {subcommand}\nwhere {subcommand} is one of:\n  " &
      join(`subcmdsId`, " ") & "\n" &
      "Run top-level cmd with the subcmd ...

dispatchMulti doesn't work with qualified symbol

Example module test:

import cligen

func get() = discard

dispatchMulti [test.get]

Error message:

stack trace: (most recent call last)
nimble/pkgs/cligen-0.9.17/cligen.nim(546) dispatchMulti
/usr/share/nim/lib/core/macros.nim(1196) $
/usr/share/nim/lib/system.nim(3928) failedAssertImpl
/usr/share/nim/lib/system.nim(3921) raiseAssert
/usr/share/nim/lib/system.nim(2968) sysFatal
test.nim(5, 1) template/generic instantiation of `dispatchMulti` from here
/usr/share/nim/lib/system.nim(2968, 7) Error: unhandled exception: /usr/share/nim/lib/core/macros.nim(1196, 17) `false` Invalid node kind nnkDotExpr for macros.`$`
test.nim(3, 6) Hint: 'test.get()[declared in test.nim(3, 5)]' is declared but not used [XDeclaredButNotUsed]

Main proc with all mandatory arguments results in compilation error.

proc foobar(option: bool) =
   discard
import cligen
dispatch(foobar)

Error:

.nimble/pkgs/cligen-0.5.0/cligen.nim(96, 34) Error: ambiguous call; both argcvt.argHelp(helpT: seq[array[0..3, string]], defVal: char, parNm: string, sh: string, parHelp: string): typed and argcvt.argHelp(helpT: seq[array[0..3, string]], defVal: bool, parNm: string, sh: string, parHelp: string): typed match for: (seq[array[0..3, string]], Error Type, string, string, string)

nested help for dispatchMulti: analog to `git help remote`

./app --help #same as before
./app --help=proc1 # only shows (autogenerated) help for proc1 (also: --help proc1, --help:proc1)

# alternative syntax (analog to `git help remote`)
./app help proc2 # only shows help for proc2

related

  • somewhat related to #22 but that issue deals with deeper nesting; my issue deals with nesting of (auto-generated) help:

  • from readme:

./cmd --help will emit a brief help message and ./cmd help emits a more comprehensive message.

not same as I'm suggesting here

A way to suppress all "short" options?

When I have a large number of options, the short options only confuse the user. What if short={} -- with an empty dictionary -- simply suppressed them all?

enum type shows 1st enum value instead of REQUIRED

type Foo = enum
  bar1,
  bar2,

type Age = int

proc main(foo: Foo, age: Age)=
  echo (foo, age)

import cligen
dispatch(main)
Usage:
  main [required&optional-params]
  Options(opt-arg sep :|=|spc):
  -h, --help                   write this help to stdout
  -f=, --foo=  enum  bar1      set foo
  -a=, --age=  int   REQUIRED  set age

i would expect
-f=, --foo= enum REQUIRED set foo in this case

[BUG] ./app myMandatory:3 => Bad value: "myMandatory:3" for option "non-option 0 (myMandatory)"; expecting int

  • mandatory arguments seem not working:
proc foobar(myMandatory: int): int =
  ##Some API call
  echo (myMandatory)
  result = 0
./app myMandatory:3
Bad value: "myMandatory:3" for option "non-option 0 (myMandatory)"; expecting int
Usage:
  foobar [optional-params] {myMandatory:int}
Some API call
  Options(opt-arg sep :|=|spc):
  --help, -?      print this help message
  • shouldn't we also support myMandatory=3 for sake of consistency?

[Feature Request] Allow a custom Options Layout

Right now the layout of the option description is something like
$long, $short (toggle|string) (""|false) $desc

I'd prefer if it was possible to change it to something like
$short, $long $desc

Its already possible to customize the usage param so it would be nice if it was possible to customize the options layout too.

How to pass version to `dispatchMulti`?

Hi,

When using dispatchGen, I tend to do the following.

    dispatchGen(main, version = ("version", "cli (" & versionString & ")"))

However when using dispatchMulti, I'm not able to do the same thing:

    dispatchMulti(
        [ upload ],
        [ create ],
        [ remove ],
        [ logs ],
        [ version ],
        # version = ("version", "cli (" & version_string & ")")
    )

Is there a way for the user interface to look like the following when using dispatchMulti:

$ cli --version
v0.1.0

generate `--help, -h` instead of `--help, -?` (or at least --help, -h, -?)

  • generate --help, -h instead of --help, -? (or at least --help, -h, -?)

  • -h is way more common than -?
    eg of commands that understand -h for help:
    EDIT:added more

nim, choosenim, nimble
# and other misc commands
brew, htop, subl, dmd, wget, curl

etc. if needed i can provide a ton more examples.

  • -? requires shell escaping

The only argument i can think of for -h is it allows short form for an arg starting with h. However that's not an issue: just generate -h for --help UNLESS another flag starts with h.
This is the way other cmds are handled:

proc foobar(bar=2.0, baz="hi"): int =
generates:
-b --bar
--baz # because -b already taken

bool variables should allow `--verb:true` and `--verb=false` etc

proc foobar(foo=1, bar=2.0, baz="hi", verb=false, paths: seq[string]): int =
  ##Some existing API call
  result = 1          # Of course, real code would have real logic here

when isMainModule: import cligen; dispatch(foobar)    # Whoa...Just one line??

I don't like toggles that can't be unset; eg it makes negating them or changing defaults awkward.
eg use case: if a default configuration uses --verb, there's no way to unset it.

the following would be backward compatible:

--verb:FOO => verb=parseBool(FOO)
--verb=FOO => verb=parseBool(FOO)

When --verb isn't followed by : or = we are still backward compatible:
for --verb FOO ..., make FOO part of next argument, eg paths in code below
for --verb (and no more arguments), set toggle as before: verb=true

proc foobar(foo=1, bar=2.0, baz="hi", verb=false, paths: seq[string]): int =
  ##Some existing API call
  result = 1          # Of course, real code would have real logic here

when isMainModule: import cligen; dispatch(foobar)    # Whoa...Just one line??

`iterates over each item of`

Have you ever seen this?

$ foo.exe --help
Usage:
  run_foo [optional-params]
iterates over each item of `a`. iterates over each item of `a`.
  Options(opt-arg sep :|=|spc):
  --help, -?                                                                                          print this help message
  --test, -t                  toggle  false                                                           set test
  --stream, -s                toggle  false                                                           set stream
  --debug, -d                 toggle  false                                                           set debug
  --silent                    toggle  false                                                           set silent
  --n_core=, -n=              int     0                                                               set n_core

I have one program which does this and one which does not. The difference seems to be that the function dispatched to is in a different module (and a different directory) in the case above.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.