Easing the Implementation of Simple Interactive CLIs for #Clojure Applications with cli4clj

tl;dr: cli4clj is a library for easing the implementation of interactive command line interfaces (CLIs) for Clojure applications. Currently, the main aim is to allow an easy implementation of a custom interactive CLI that is easily usable, even by people unfamiliar with Clojure. cli4clj is already available via Clojars.

This post is intended as a brief announcement of cli4clj. As my time is currently, unfortunately, pretty limited I cannot elaborate in depth on all the details. So, I will try to briefly outline my motivation for creating cli4clj, its purpose, (current) features, and how to use it.

Personally, I think that interactive CLIs are great, especially for experimenting and playing around with ideas. This is maybe one reason why I like to work with Clojure and its REPL.

In the past, I already added simple interactive CLIs to some of my applications and prototypes as they allow nice and easy interaction at run-time. An example is my Clojure clj-net-pcap prototype. However, my current implementation of this particular CLI is, well, lets say, very simple and naive. While this implementation approach of the CLI was sufficient back when I started clj-net-pcap, the CLI became increasingly complex over time.

For languages like Java there are libraries like cliche or the Spring Shell that aim on easing the implementation of “in-application” interactive CLIs. Unfortunately, I could not find an equivalent for Clojure that suited my needs.

While the Clojure REPL can also be started from within any third-party application, having a full-featured REPL as in-application CLI was too much for my taste. There is some quote that, “with great power comes great responsibility.” In a sense I invert this principle for cli4clj in the way that I want cli4clj to be easily usable, from implementation and CLI-usability perspective, (without having the user to worry about great or complex duties/responsibilities) and thus purposely reduce the set of available features as compared to the default Clojure REPL (limit the powers of the CLI). The result is a simple and easily implementable interactive CLI that can also be used by people who are not familiar with Clojure.

In the following listing, a simple example is shown. In this example, a command named “test” is defined for the CLI that simply prints a test message. You can also see definitions for short and long information texts that will be printed via the integrated “help” command.

(defn -main [& args]
  (let [cli-opts {:cmds {:test {:fn #(println "This is a test.")
                                :short-info "Test Command"
                                :long-info "Print a test message."}}}]
    (start-cli cli-opts)))

In the CLI, this looks as follows:

cli# test
This is a test.

Commands can also have arguments as shown in the next example for the “add” command. In addition, this example shows the definition of an alias, “a”, for being used as a shortcut to “add”.

(defn -main [& args]
  (let [cli-opts {:cmds {:add {:fn (fn [a b] (+ a b))
                               :short-info "Add two values."}
                         :a :add}}]
    (start-cli cli-opts)))

In the CLI, this looks as follows:

cli# add 1 2
cli# a 2 3

The functions that implement the actual functionality of the commands can be globally, locally, or anonymously defined. In my opinion, the capability to use locally and anonymously defined functions is great because it, e.g., allows to easily leverage closures for the CLI functionality.

A more complex and commented example of how cli4clj is intended to be used can be found in the example in the cli4clj GitHub repository. Furthermore, you can see some example output of a CLI session using this example code in the README on GitHub.

Current features of cli4clj are, e.g.:

  • Simple configuration via a map
  • Easily usable interactive CLI
  • Allows to use globally, locally, and anonymously defined functions
  • Clojure data types for command arguments, e.g., int, float, vector, list, set, …
  • Aliases for commands enable, e.g., shortcuts
  • Help command for displaying information about available commands.
  • Based on the Clojure REPL
  • Customizable similarly to the Clojure REPL

So far, I just put a few hours into implementing and documenting cli4clj. However, I am already very happy with the result, its features, and the usability. I also already uploaded cli4clj to Clojars. In the future, I plan to further work on cli4clj and improve it. One idea could be, e.g., to support named arguments like many Linux commands do, in the way “–foo” or “-f”, and to supply default values for options that are not explicitly set.

I hope that you find cli4clj helpful. All feedback, ideas, and contributions are highly appreciated.

This entry was posted in Announcements, cli4clj, Libs. and tagged , , , , . Bookmark the permalink.

1 Response to Easing the Implementation of Simple Interactive CLIs for #Clojure Applications with cli4clj

  1. Pingback: Update on cli4clj for easing the implementation of CLIs in Clojure | ruedigergad

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.