$ lein.sh run -r option2
{:options {:random option1}, :arguments [322 chunky], :summary -h, --help Show this screen.
--version Show version.
-d, --debug Debug information.
-r, --random OPTION option1 Choose random option,
:errors [Failed to validate "-r option2": Valid options are option1 | option2]}
It looks that the string argument (“option2” in this example) fails the validation. And i don’t understand why… I have tried to search about this error, because i imagine that must be a typical novice error, but i haven’t luck with that. Somebody can give me a tip about how to fix this code.
:validate-fn A vector of functions that receives the parsed option value
and returns a falsy value or throws an exception when the
value is invalid. The validations are tried in the given
order.
So the function above will get a parsed option as its argument. Let’s expand the terse hash-percent syntax to see what’s going on here.
Hmm, something’s not right. In the current shape the function will call random-options? with two arguments. That’s most likely not what we want here.
Instead, we’d probably want to call random-options? with a single argument, which we obtain by calling string/lower-case on parsed-option. Something like:
But that’s still not what we need. Notice that the function above gets a parsed option as its argument. In other words, the argument will be firstly processed by the :parse-fn you specified above.
Your parsing function maps strings to keywords. Given "option1" it returns :option1. As a result, your validating function will get a keyword as its argument. Is that what the validating function expects?
HTH and good luck debugging. Feel free to follow up if you run into any other problems!
Also, your parse-fn converts the string input to a keyword (either :option1 or :option2) and parsing happens before validation so by the time you call validate-fn, you have keywords, not strings.
You could achieve this more simply with:
:parse-fn keyword ; convert string to keyword
:validate-fn #{:option1 :option2} ; set as predicate: returns truthy if argument is a member of the set
I have just discovered clj-matic as well. Have to say that the :ednfile option (and similar ones) are the biggest time savers ever No more copy and paste yeah!
I think that tools.cli is a different thing. It does one thing very well. CLI-matic has a broader scope, and is more opinionated - let’s trade some flexibility for convenience, just plug it in and think about something else.
Thanks for the tip about cli-matic, although after solving my silly bug with your help, everything is coming nice and easy, and tools.cli is working perfectly. Great library Mr. @seancorfield!!!
For reference, the example code bugfixed is:
(ns random-options.core
(:require [clojure.string :as string]
[clojure.tools.cli :refer [parse-opts]])
(:gen-class))
;; --------------------------------------------------------------------------
;; In the real script, there is a few those maps with lambda
;; functions, those are used for choosing the quantization
;; algorithm, select endianness, ...
(def random-options {:option1 #(%)
:option2 #(%)})
;; --------------------------------------------------------------------------
(def cli-options
[["-h" "--help" "Show this screen."]
[nil "--version" "Show version."]
["-d" "--debug" "Debug information."]
["-r" "--random OPTION" "Choose random option"
:default "option1"
:parse-fn keyword
:validate [#(contains? random-options %) "Valid options are option1 | option2"]]])
;; --------------------------------------------------------------------------
(defn -main [& args]
(println (parse-opts args cli-options)))