Coder Social home page Coder Social logo

l3nz / cli-matic Goto Github PK

View Code? Open in Web Editor NEW
354.0 354.0 29.0 403 KB

Compact, hands-free [sub]command line parsing library for Clojure.

License: Eclipse Public License 2.0

Clojure 96.69% Shell 3.31%
argv-parser clojure command-line command-line-parser graalvm planck subcommands

cli-matic's People

Contributors

agilecreativity avatar allcontributors[bot] avatar borkdude avatar cortys avatar ieugen avatar jeiwan avatar jwhitlark avatar l3nz avatar lread avatar noahtheduke avatar the-alchemist avatar timokramer avatar ty-i3 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

cli-matic's Issues

Error with new

Exception in thread "main" clojure.lang.ExceptionInfo: Call to #'cli-matic.help-gen/generate-help-possible-mistypes did not conform to spec:
core.cljc:364

-- Spec failed --------------------

Return value

  [... ... ... [" vendite"]]
               ^^^^^^^^^^^^

should satisfy

  string?

But it's actually a vector of strings OR vectors of strings. So it works but spec is wrong.

Command validation using spec

We should be able to validate all options to a command using spec, with a better error message than the default one.

Multiline descriptions for options.

Thanks for this library @l3nz, it's really quite fantastic how easy and concise it makes all this stuff, I added an improved CLI interface to cljdoc using it: cljdoc/cljdoc@1ce56b8

For some subcommands I'd like to specify longer descriptions, spanning a couple of lines. I was wondering if there's any tooling or plans around this?

I could construct a :description string with linebreaks myself but I think cli-matic could help with the following aspects:

  • trim leading whitespace in some smart way (maybe codox' approach is sufficient)
  • automatically break lines longer than 80 characters
  • maintain the one-character indentation under NAME:

FWIW there could also be a new key :usage for longer text, reserving :description for a one-line overview.

Unrelated feedback: it took me a while to spot the :default :present variant in the README when trying to figure out how to make options mandatory. Maybe introducing another key :required? or so would make this easier.

Boolean (flag) data types

I see a few boolean options:

  • flags like --flag (true) or --no-flag (false). The first one could be shortened to -f
  • flags with parameters, like --flag=1. Would expect to recognise "Y", "Yes", "On", "T", "True" and "1" as truthy. Falsey would be "N", "No", "Off", "F", "False" and "0". Other values would raise an error.

Positional parameters

Why don't we use the same mechanism we use for options to pull positional argument?

Like - instead of:

{:option "pa" :short "a" :as "Parameter A" :type :int :default 0}

we could have

{:option "pa" :short 0 :as "Parameter A" :type :int :default 0}

This would use the first free parameter, store it in "pa" and check it's an int.

Pulled positional arguments remain anyway available in the arguments vector.

Positional arguments are only valid on sub-commands and must appear on the help line.

Example with GraalVM

I think we should have an example/tutorial of how to make a simple Cli app that runs within GraalVM. I am sure it would have a decent success and in the end it's what everybody would love - simple CLI commands that are small, portable and start up in no time.

Better startup times: skip macros

It would be worthwhile too add some tips section for better startup times, e.g.

time clj -J-Dclojure.spec.skip-macros=true -m recap sv

real	0m2.587s - user	0m6.997 - sys	0m0.332s

versus default:

time clj -J-Dclojure.spec.skip-macros=false -m recap sv

real	0m3.141s -user	0m8.707s -sys	0m0.391s


Writing it here so I don't forget ๐Ÿ˜ธ

Multiline description

We could have an optional multi-line description added to the utility and to each subcommand, to be displayed in the help screens.

Multiple aliases for commands

We could have multiple aliased for each subcommand, like e.g.

COMMANDS:
 add, a     adds 
 sub, s     subtracts

So writing

mycmd add --a 1 --b 2

and

mycmd a --a 1 --b 2

are the very same thing.

Can't change/establish root binding of: *explain-out*

Caused by: java.lang.IllegalStateException: Can't change/establish root binding of: explain-out with set
at clojure.lang.Var.set(Var.java:223)
at cli_matic.optionals$orchestra_instrument.invokeStatic(optionals.clj:70)
at cli_matic.optionals$orchestra_instrument.invoke(optionals.clj:56)
at cli_matic.presets__init.load(Unknown Source)
at cli_matic.presets__init.(Unknown Source)

Don't instrument functions by default

cli-matic.core calls orchestra.spec.test/instrument when being loaded. This instruments all spec'ed functions and not just cli-matic's own API. In my case this brought up surprising issues where I didn't expect them.

Would you consider a PR where only cli-matic's own functions are instrumented?

Planck: Remove exceptions

These should not be there:

WARNING: Use of undeclared Var cli-matic.core/Throwable at line 74 cli_matic/core.cljc
WARNING: Use of undeclared Var cli-matic.core/Throwable at line 233 cli_matic/core.cljc
WARNING: Use of undeclared Var cli-matic.core/IllegalAccessException at line 422 cli_matic/core.cljc
WARNING: Use of undeclared Var cli-matic.core/IllegalAccessException at line 422 cli_matic/core.cljc
WARNING: Use of undeclared Var cli-matic.core/IllegalAccessException at line 454 cli_matic/core.cljc
WARNING: Use of undeclared Var cli-matic.core/IllegalAccessException at line 454 cli_matic/core.cljc
WARNING: Use of undeclared Var cli-matic.core/Throwable at line 524 cli_matic/core.cljc

Short names for options (aliases)

We want to have shorter and optional names for both options and subcommands.

The names should appear in the online help and of course be used at all.

Use tools.cli v 0.4.1

Not sure whether there are any changes that affect us, but it's better to use the current version in any case.

Use separate namespaces

It would make sense to have separate namespaces for:

  • general utils
  • code that generates help text (that is rather big)

This way we could have a smaller core.

It is just refactoring and tests.

Add EDN support

Wouldn't be optional, as it's included with Clojure. I'm working on it.

Support for STDIN

I hacked something together that looks like

 (let
   [bf (java.io.BufferedReader. *in*)]
    (if
     (.ready bf)
      (let
       [conf (slurp *in*) vargs (into [] args)]
        (run-cmd (conj vargs "--stdin" conf) STDIN_INPUT))
      (run-cmd args FILE_INPUT))))

where STDIN_INPUT

(def
  STDIN_INPUT
  {:app
   {:command "nparser",
    :description "A command-line configuration generator",
    :version "0.1.2"},
   :global-opts [],
   :commands
   [{:command "to-json",
     :description "Generate JSON from a config",
     :opts
     [{:option "stdin",
       :as "Config input file",
       :type :string,
       :default :present}
      {:option "grammar",
       :as "Grammar file",
       :type :string,
       :default :present}],
     :runs gen-json}
    {:command "to-config",
     :description "Generate config from an input file",
     :opts
     [{:option "stdin",
       :as "JSON input file",
       :type :string,
       :default :present}],
     :runs gen-config}]})

not very idiomatic, but would be great to incorporate this into cli-matic..

The primary use-case is now that we have GRAALVM we can native-compile CLJ code into binary artifacts to build command-line apps..

JSON parameters

Why not having

    --data '{"a": "b"}'

that returns {:data {"a" "b"}} as a parameter, as long as you have Cheshire on the classpath?

And why you are at it, why not having

    --data @somedata.json

That returns the contents of somedata.json as a parsed structure?

Lots of useless output in 0.1.16

"SUB: globals" [{:option "base", :as "The number base for output", :type :int, :default 10}]
"SUB: def" [{:option "a1", :short "a", :env "AA", :as "First addendum", :type :int, :default 0} {:option "a2", :short "b", :as "Second addendum", :type :int, :default 0}]
"Spec for subcmd " #object[clojure.core$constantly$fn__5394 0x79c3f01f "clojure.core$constantly$fn__5394@79c3f01f"]
"Validating Specs" [{:option "base", :as "The number base for output", :type :int, :default 10}] {:base 16, :a1 1, :a2 254, :_arguments []}
"Validating Specs" [{:option "a1", :short "a", :env "AA", :as "First addendum", :type :int, :default 0} {:option "a2", :short "b", :as "Second addendum", :type :int, :default 0}] {:base 16, :a1 1, :a2 254, :_arguments []}
"Failing global" nil
"Failing local" nil
"Failing total" nil

We don't want that.

Add shutdown hook

If the command run is long-running, it would be nice to add a shutdown hook by calling :on-shutdown:

(.addShutdownHook
            (Runtime/getRuntime)
            (Thread.
              (fn []
                (log "Bye!")
                )))

Read values from environment variables

If an option is tagged as ':env ABCD'; it will

  • load the default from ABCD (but it can be overridden through the command line)
  • display "ABCD" in the help for that option

Regression: Spec fails on 0.1.13

This is because we call rewrite-opts with three parameters in get-options-summary.
Fix is easy, but how can we know it will not happen again?

Add YAML support (optional like json)

I do a lot of work with Kubernetes stuff, and YAML is the preferred format.

I'm working on this in my fork; I'll open a pull request when it's ready.

Set data types

Sometimes you have a given set of string values that are the only available options.

We want to:

  • display the allowed set on help
  • cast the string to a keyword
OPTIONS:
   -k, --kind S*         Kind of mailing: (:Q :Q-REN :Q-UPG)

Do not abort on wrong return type.

If we return anything from our main method that is not a number, it crashes!

JVM Exception: #error {
 :cause clojure.lang.PersistentArrayMap cannot be cast to java.lang.Number
 :via
 [{:type java.lang.ClassCastException
   :message clojure.lang.PersistentArrayMap cannot be cast to java.lang.Number
   :at [clojure.lang.Numbers isZero Numbers.java 92]}]
 :trace
 [[clojure.lang.Numbers isZero Numbers.java 92]
  [cli_matic.core$invoke_subcmd invokeStatic core.clj 412]
  [cli_matic.core$invoke_subcmd invoke core.clj 405]
  [cli_matic.core$run_cmd_STAR_ invokeStatic core.clj 442]
  [cli_matic.core$run_cmd_STAR_ invoke core.clj 428]
  [cli_matic.core$run_cmd invokeStatic core.clj 447]
  [cli_matic.core$run_cmd invoke core.clj 444]

We should just ignore them and return zero.

Using :default for mandatory options

We have three cases:

  • a totally optional option, no default - if there is it appears in opts, if not it's not there - you just don't specify a :default
  • a totally optional option, with default - it always appears in option map (worst case with default value) - e.g. :default 3
  • a mandatory option without default - any value is good but you must set it - we could handle it by setting :default :present

In the third case, we will abort with an error message if item is not present in after-parse option map.

Missing global options do not cause errors if you call a subcommand only for getting its help page.

Interactive mode - why not?

If we have:

OPTIONS:
   -a, --a1 N  0  First addendum [$AA]
   -b, --a2 N  0  Second addendum
   -?, --help

Why shouldn't we have:

$ toycalc add --interactive

First addendum (a1, def 0): 10
Second addendum (a2, def 0): 20

You are about to add 10 and 20 together. Are you sure you want to continue? Y
30

Add :multiple tag for options

If an option is tagged as :multiple true, then it is actually a vector of all parameter invocations.

     cmd sub --a 1 --a 2 --a 3

where :a is tagged as :multiple, should get you

      {:a [1 2 3]}

In the order they are passed.

Slurping of whole file at once

We should have an option to slurp a whole file at once given a filename, and return it as big string, vector of strings or decoded-json (with optional deps on cheshire).

Feature Request: Ability to override generation help text

This feature request adds optional functions that override the way cli-matic prints help, affording complete byte-by-byte control over the help text.

Rationale: Our internal CLI tooling needs to adhere to certain policies regarding help and usability for developers. Certain info such as Git build, version string, and grouping of commands needs to be printed in the prescribed format. This feature request would realize the ability to completely control the presentation of generated help text to satisfy those policies.

An example of generating global help:

(def banner
  (str "Welcome to CLI-ABC123, Git "
       build-info/git-hash))

(defn commands-segment
  [cfg]
  (str "Commands:\n"
       (clojure.string/join
         "\n"
         (map
           #(format "   %-10s %s"
                    (get % :command)
                    (get % :description))
           (cfg :commands)))))

(defn my-global-help
  [cfg]
  (clojure.string/join "\n" [banner (commands-segment cfg) ...]))

and using it as the global help text generator:

(run-cmd args {:app {:global-help my-global-help
                     ...}})

Have double :short option.

We should get a better message out.

Exception in thread "main" java.lang.AssertionError: Assert failed: (distinct?* (remove nil? (map :short-opt %)))
	at clojure.tools.cli$compile_option_specs.invokeStatic(cli.clj:265)
	at clojure.tools.cli$compile_option_specs.invoke(cli.clj:265)
	at clojure.tools.cli$parse_opts.invokeStatic(cli.clj:593)
	at clojure.tools.cli$parse_opts.doInvoke(cli.clj:471)
	at clojure.lang.RestFn.invoke(RestFn.java:425)
	at cli_matic.core$parse_cmds.invokeStatic(core.clj:417)
	at cli_matic.core$parse_cmds.invoke(core.clj:372)
	at clojure.lang.AFn.applyToHelper(AFn.java:156)
	at clojure.lang.AFn.applyTo(AFn.java:144)
	at orchestra.spec.test$spec_checking_fn$fn__727.doInvoke(test.clj:123)
	at clojure.lang.RestFn.invoke(RestFn.java:421)
	at cli_matic.core$run_cmd_STAR_.invokeStatic(core.clj:521)
	at cli_matic.core$run_cmd_STAR_.invoke(core.clj:519)
	at cli_matic.core$run_cmd.invokeStatic(core.clj:543)
	at cli_matic.core$run_cmd.invoke(core.clj:535)

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.