Hello,
I’m new to Clojure and programming without mutation in general.
I’m building a small command line tool using Clojure; and I’m parsing the arguments passed using clojure.tools.cli
. Now some of the arguments I want to make as compulsory, and if user doesn’t pass those argument I want to print error message and exit.
Here’s snippet of the function which does this:
(defn parse-args
[args]
(let [cli-options
[["-f" "--file \"path/to/file\"" :required "File"
:id :file]
["-d" "--description \"description here\"" "Description"
:id :desc
:default "NA"]
["-t" "--tags \"Tag:SubTag\"" "Tags"
:id :tags
:default "Untagged"]
["-h" "--help"]]
parsed-args (parse-opts args cli-options)
duration (-> parsed-args :arguments (get 0))
file-path (-> parsed-args :options :file)
tags (-> parsed-args :options :tags (clojure.string/split #":"))
desc (-> parsed-args :options :desc)
errors (-> parsed-args :errors)
errors (if (nil? file-path) (conj errors "No log file was specified (please use -f)") errors)
errors (if (nil? duration) (conj errors "No timer duration was specified.") errors)]
[duration file-path tags desc errors]))
main
calls this functions and simply exits if errors
is nil
.
Notice how I’m rebinding errors
in let
. This works, but I guessed this isn’t a good way of doing it, so I improved it a little bit by passing a vector of field
and err-msg
and reducing the vector to a errors
list:
(defn parse-args
[args]
(let [cli-options
[["-f" "--file \"path/to/file\"" :required "File"
:id :file]
["-d" "--description \"description here\"" "Description"
:id :desc
:default "NA"]
["-t" "--tags \"Tag:SubTag\"" "Tags"
:id :tags
:default "Untagged"]
["-h" "--help"]]
parsed-args (parse-opts args cli-options)
duration (-> parsed-args :arguments (get 0))
file-path (-> parsed-args :options :file)
tags (-> parsed-args :options :tags (clojure.string/split #":"))
desc (-> parsed-args :options :desc)
parse-errors (-> parsed-args :errors)
errors (reduce (fn [errors, [field err-msg]]
(if (nil? field) (conj errors, err-msg) errors))
parse-errors
(list [file-path "No log file was specified (please use -f)"]
[duration "No timer duration was specified."]))]
[duration file-path tags desc errors]))
Now, I’ve three questions:
- Is there any way to make an argument required in
clojure.tools.cli
- How to handle such situations, where in I have to repeatedly mutate a
symbol
- In above example how could I have passed the vector using
->
like macro. For examplereduce
in Elixir, takeenumerable
as first argument, so we can use|>
operator (which I think is comparable to->
in Clojure) to push a list toEnum.reduce
and apply a function.