@@ -24,6 +24,21 @@ or add the following to your `project.clj` ([Leiningen](https://leiningen.org/))
2424```
2525<!-- /installation -->
2626
27+ ## Rationale
28+
29+ This is an opinionated CLI argument handling library. It is meant for command
30+ line tools with subcommands (for example git, which has ` git commit ` , ` git log `
31+ and so forth). It tries to sticks to common conventions (in particular the
32+ prominent GNU conventions), uses a syntax that should feel natural to Clojure
33+ programmers (it's all just functions and data), and provides your tool with
34+ built-in help facilities automagically.
35+
36+ It is Babashka compatible, and in fact pairs really nicely with ` bb ` for making
37+ home-grown or general purpose tools.
38+
39+ This library helps you write [ Well-behaved Command Line
40+ Tools] ( https://lambdaisland.com/blog/2020-07-28-well-behaved-command-line-tools )
41+
2742## Getting Started
2843
2944Here, we will explain how to use ` com.lambdaisland/cli ` to create a simple script that can print out the options it receives and handle basic help.
@@ -33,23 +48,24 @@ Here, we will explain how to use `com.lambdaisland/cli` to create a simple scrip
3348Init the ` bb.edn ` with
3449
3550```
36- {:deps {com.lambdaisland/cli {:mvn/version "0.24.97"}}}
51+ {:deps {com.lambdaisland/cli {:mvn/version "0.24.97"}}}
3752```
3853
3954Create a file (e.g., ` cli-test.bb ` ):
4055
4156```
4257#!/usr/bin/env bb
4358
44- (require '[lambdaisland.cli :as cli]
45- '[clojure.pprint :as pprint])
59+ (require
60+ '[lambdaisland.cli :as cli]
61+ '[clojure.pprint :as pprint])
4662
4763;; 1. Define your main function.
4864(defn cli-test
4965 "Ground breaking tool breaks new ground."
50- [flags ]
51- (pprint/pprint flags ))
52-
66+ [opts ]
67+ (pprint/pprint opts ))
68+
5369;; 2. Dispatch the function (using its var).
5470(cli/dispatch #'cli-test)
5571```
@@ -69,29 +85,39 @@ By passing a function var (`#'cli-test`), the library automatically infers the n
6985
7086### Step 2: Pass Arguments and Flags
7187
72- Your command function receives all parsed input as a single map argument, conventionally named flags or opts.
73-
7488Run it with some input
7589
7690```
77- $ bb cli-test.bb --abc hello world --format=txt
78- {:lambdaisland.cli/sources {},
79- :lambdaisland.cli/argv ["hello" "world"],
80- :abc 1,
91+ $ bb cli-test.bb --abc -xxy hello --format=txt world
92+ {:lambdaisland.cli/argv ["hello" "world"]
93+ :abc 1
94+ :x 2
95+ :y 1
8196 :format "txt"}
8297```
8398
84- Flags beginning with ` -- ` become map keys; remaining values are collected in ` :lambdaisland.cli/argv ` .
99+ Your command function receives all parsed input as a single map argument.
100+ Arguments starting with ` - ` or ` -- ` are parsed as flags with some default
101+ conventions (you can change how they are handled by explicitly defining them).
102+
103+ - ` --abc ` — the value in the map is the number of times the flag, can be treated as a count or simply as a boolean
104+ - ` -x ` — also a count, but multiple one-letter flags can be passed at once when using a single dash, so ` -xxy ` is the same as ` -x -x -y `
105+ - ` --format=txt ` — key-value, if the value looks like a number it is parsed as such
106+ - remaining values are collected in ` :lambdaisland.cli/argv ` .
85107
86108### Step 3: Define Custom Flags
87109
88- To gain control over how flags are parsed and to provide richer help text, we wrap the command var in a configuration map.
110+ To gain control over how flags are parsed and to provide richer help text, we
111+ wrap the command var in a configuration map. Note that ` :flags ` uses vector
112+ syntax, so you can specify the order they show up in the help, but it functions
113+ more like a map.
89114
90115Modify your ` cli-test.bb ` to include a ` :flags ` configuration:
91116
92117```
93118(cli/dispatch
94- {:command #'cli-test ; The function to call
119+ {:name "cli-test"
120+ :command #'cli-test ; The function to call
95121 :flags ["-v, --verbose" "Increases verbosity"
96122 "--input FILE" "Specify the input file"]})
97123```
@@ -115,38 +141,93 @@ Run the tool with the new flags:
115141
116142```
117143$ bb cli-test.bb -vvv --input=world.txt
118- {:lambdaisland.cli/sources
119- {:verbose "-v command line flag", :input "--input command line flag"},
120- :lambdaisland.cli/argv [],
144+ {:lambdaisland.cli/argv [],
121145 :verbose 3,
122146 :input "world.txt"}
123147```
124148
125- Multiple short flags like ` -vvv ` are counted automatically, producing ` :verbose 3 ` .
149+ ### Step 4. Flag options and subcommands
126150
127- ### Summary
151+ To give a hint of the full power of this library, we'll add a few subcommands,
152+ and change how the flags behave.
128153
129- - Step 1: Start with a single command using ` cli/dispatch ` .
130- - Step 2: Pass arguments and flags; everything becomes a map.
131- - Step 3: Add ` :flags ` for option parsing and richer help.
154+ For this we replace the single ` :command ` with a set of ` :commands ` , using a
155+ ` tool noun verb ` structure that is a common convention.
132156
133- You now have a solid foundation for building more advanced multi-command tools.
157+ Instead of just a string, we use a map to describe each flag, so we can set a
158+ default, change how it is parsed, or set the key that will show up in the
159+ options map passed to the command function.
134160
135- ## Rationale
161+ ``` clj
162+ (defn ls-widgets
163+ [opts]
164+ (println " Listing widgets" )
165+ (pprint/pprint opts))
136166
137- This is an opinionated CLI argument handling library. It is meant for command
138- line tools with subcommands (for example git, which has ` git commit ` , ` git log `
139- and so forth). It works exactly how we like it, which mostly means it sticks to
140- common conventions (in particular the prominent GNU conventions), needs little
141- ceremony, and provides your tool with built-in help facilities automagically.
167+ (cli/dispatch
168+ {:name " cli-test"
169+ :commands [" widget"
170+ {:doc " Manipulate widgets"
171+ :commands
172+ [" ls"
173+ {:doc " List widgets"
174+ :command ls-widgets}]}]
175+ :flags [" -v, --verbose" {:doc " Increases verbosity"
176+ :key :verbosity
177+ :default 0 }
178+ " --format <fmt>" {:doc " Specify the format"
179+ :default " txt"
180+ :parse keyword}]})
181+ ```
182+
183+ And try it out:
184+
185+ ```
186+ $ cli-test widget ls --format=xxx -vvv
187+ Listing widgets
188+ {:verbosity 3
189+ :format :xxx
190+ :lambdaisland.cli/command ["widget" "ls"]
191+ :lambdaisland.cli/argv []}
192+ ```
142193
143- It is Babashka compatible, and in fact pairs really nicely with ` bb ` for making
144- home-grown or general purpose tools.
194+ But note that this example can also be written using vars and docstrings, which
195+ makes it really neat. It also lets you put command specific flags in var
196+ metadata, like this:
145197
146- It scales from extremely low ceremony basic scripts, to fairly complex setups.
198+ ``` clj
199+ (defn ls-widgets
200+ " List widgets"
201+ {:flags [" --[no-]disabled" " Include disabled widgets in the list" ]}
202+ [opts]
203+ (println " Listing widgets" )
204+ (pprint/pprint opts))
205+
206+ (def widget-cmds
207+ " Manipulate widgets"
208+ {:commands [" ls" #'ls-widgets]})
209+
210+ (def flags
211+ [" -v, --verbose" {:doc " Increases verbosity"
212+ :key :verbosity
213+ :default 0 }
214+ " --format <fmt>" {:doc " Specify the format"
215+ :default " txt"
216+ :parse keyword}])
147217
148- This library helps you write [ Well-behaved Command Line
149- Tools] ( https://lambdaisland.com/blog/2020-07-28-well-behaved-command-line-tools )
218+ (cli/dispatch
219+ {:name " cli-test"
220+ :commands [" widget" #'widget-cmds]
221+ :flags flags})
222+ ```
223+
224+ ### Summary
225+
226+ - Step 1: Start with a single command using ` cli/dispatch ` .
227+ - Step 2: Pass arguments and flags; everything becomes a map.
228+ - Step 3: Add ` :flags ` for option parsing and richer help.
229+
230+ You now have a solid foundation for building more advanced multi-command tools.
150231
151232## How-to Guides
152233
0 commit comments