Clojure: using clojure/tools.cli to parse command line arguments
For my HTTP server project I want the option of configuring it on the command line for at least the port, document root and potentially IP to listen on (or all). I came across Building a Clojure app with a command-line interface? on StackOverflow which informed me that clojure.tools.cli was the right path to take over the with-commmand-line.
Here is an example of using clojure/tools-cli which revealed some Clojure syntax I’d overlooked (more on that later):
1
2
3
4
5
6
7
8
9
10
11
(ns server.core
(:gen-class)
(:require [clojure.tools.cli :as c]))
(defn -main [& args]
(let [[options args banner]
(c/cli args
["-port" "Port to listen on" :default 5000]
["-root" "Root directory of web server" :default "public"])]
(println "port:" (:port options))
(println "root:" (:root options))))
-
options is a hash-map of the parsed options
-
args is the other arguments on the command line
-
banneris a string one can print for command line options – for this program, it would contain:
Usage:
Switches Default Desc ——– ——- —- -port 5000 Port to listen on -root public Root directory of web server
Note that tools.cli throws an exception when an unrecognized command line option is provided like so:
1
2
Exception in thread "main" java.lang.RuntimeException:
java.lang.Exception: '-z' is not a valid argument
I guessed it would default to showing the banner it generates but it does not do so.
Basic Clojure Syntax I wasn’t aware one could take a returned vector and assign its contents to multiple variables with let as done in this line from above:
1
(let [[options args banner] ...
In a prior post, I explained how I was passing back a response to an HTTP request as a hash-map of hash-maps. This can start to get confusing and I think the approach used above can make this simpler – instead of a hash-map of hash-maps consider returning a vector of hash-maps.