Skip to content

Commit fbe0833

Browse files
committed
Switch to using a single map as the command input
1 parent 93599d8 commit fbe0833

3 files changed

Lines changed: 296 additions & 229 deletions

File tree

repl_sessions/test_scratch.clj

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
(ns test-scratch
2+
(:require [clojure.test :as t]))
3+
4+
5+
6+
(defmacro def-are [name & pairs]
7+
`(deftest ~name
8+
~@(for [[a b] (partition 2 pairs)]
9+
(is (= ~b ~a)))))
10+
11+
(set! *print-namespace-maps* false)
12+
13+
(def last-flags (atom nil))
14+
15+
(defn capture-cmd [flags]
16+
(reset! last-flags flags))
17+
18+
(defn cli [cli-args cmdspec]
19+
(reset! last-flags :not-called)
20+
(let [out (with-out-str
21+
(dispatch cmdspec cli-args))]
22+
(if (seq out)
23+
[@last-flags out]
24+
@last-flags)))
25+
26+
(defn lines [& args]
27+
(str/join "\n" args))
28+
29+
(def cmds-run ["run" {:description "Do the thing"
30+
:command capture-cmd}])
31+
32+
(def flags-input ["-i,--input FILE" "Specify input file"])
33+
34+
(def-are base-case
35+
(cli ["run"] {:commands cmds-run})
36+
{:lambdaisland.cli/command ["run"], :lambdaisland.cli/argv []})
37+
38+
(def-are help-rendering
39+
(cli [] {:commands cmds-run})
40+
[:not-called
41+
(lines "Usage: cli [command...] [flags-or-args...]"
42+
""
43+
" run Do the thing"
44+
"")]
45+
46+
(cli ["help"] {:commands cmds-run})
47+
[:not-called
48+
(lines "Usage: cli [command...] [flags-or-args...]"
49+
""
50+
" run Do the thing"
51+
"")]
52+
53+
(cli ["help"] {:commands cmds-run})
54+
[:not-called
55+
(lines "Usage: cli [command...] [flags-or-args...]"
56+
""
57+
" run Do the thing"
58+
"")]
59+
(cli ["--help"] {:commands cmds-run
60+
:flags flags-input})
61+
[:not-called
62+
(lines "Usage: cli [command...] [flags-or-args...]"
63+
""
64+
" -i,--input FILE Specify input file"
65+
""
66+
" run Do the thing"
67+
"")])
68+
69+
(def-are arguments
70+
(cli ["run" "hello"] {:commands cmds-run})
71+
{:lambdaisland.cli/command ["run"]
72+
:lambdaisland.cli/argv ["hello"]})
73+
74+
(deftest subcommands
75+
(let [cmdspec {:commands {"run" {:command capture-cmd
76+
:description "Do something"}
77+
"widget" {:description "Work with widgets"
78+
:commands
79+
["ls" {:description "List widgets"
80+
:command capture-cmd}
81+
"add" {:description "Add widget"
82+
:command capture-cmd}]}}}]
83+
(is (= [["x"] {::cli/command ["widget" "ls"]}]
84+
(cli ["widget" "ls" "x"] cmdspec)))))
85+
86+
(deftest flags
87+
(is (= [nil {:input "INPUT"
88+
:output "OUTPUT"
89+
::cli/command ["run"]}]
90+
(cli
91+
["run" "--input" "INPUT" "--output" "OUTPUT"]
92+
{:commands {"run" {:command capture-cmd
93+
:description "Do something"}}
94+
:flags ["-i,--input FILE" {:desc "Specify input file"}
95+
"--output FILE" "Specify output file"]}))))
96+
97+
(deftest parse-flagstr-test
98+
(testing "single short flag"
99+
(is (= {:flagstr "-i"
100+
:flags ["-i"]
101+
:description "Specify input file"
102+
:argdoc ""
103+
:key :i
104+
:argcnt 0
105+
:args []}
106+
(parse-flagstr "-i" {:description "Specify input file"}))))
107+
(testing "single long flag"
108+
(is (= {:flagstr "--input"
109+
:flags ["--input"]
110+
:description "Specify input file"
111+
:argdoc ""
112+
:key :input
113+
:argcnt 0
114+
:args []}
115+
(parse-flagstr "--input" {:description "Specify input file"}))))
116+
117+
(testing "short and long - with argument"
118+
(is (= [
119+
{:flag "-i"
120+
:short? true
121+
:args '(:file)
122+
:key :input
123+
:argcnt 1
124+
:description "Specify input file"}
125+
{:flag "--input"
126+
:short? false
127+
:args '(:file)
128+
:key :input
129+
:argcnt 1
130+
:description "Specify input file"}]
131+
(build-flagmap-entries ["-i,--input FILE" {:description "Specify input file"}]))))
132+
133+
(testing "override key, value"
134+
(is (= [{:flag "--foo"
135+
:short? false
136+
:args ()
137+
:key :bar
138+
:argcnt 0
139+
:description "Do a foo"
140+
:value 123}]
141+
(build-flagmap-entries
142+
["--foo" {:description "Do a foo"
143+
:key :bar
144+
:value 123}]))))
145+
146+
(testing "--[no-]"
147+
(is (= [{:flag "--no-foo"
148+
:short? false
149+
:args ()
150+
:key :foo
151+
:argcnt 0
152+
:value false
153+
:description "Enable/disable foo"}
154+
{:flag "--foo"
155+
:short? false
156+
:args ()
157+
:key :foo
158+
:argcnt 0
159+
:value true
160+
:description "Enable/disable foo"}]
161+
(build-flagmap-entries ["--[no-]foo" {:description "Enable/disable foo"}])))))
162+
163+
(deftest parse-arg-names-test
164+
(is (= '[["-i"] "" ()] (parse-arg-names "-i")))
165+
(is (= '[["-i"] " FOO BAR" (:foo :bar)] (parse-arg-names "-i FOO BAR")))
166+
(is (= '[["-i" "--input"] "=<foo>" (:foo)] (parse-arg-names "-i, --input=<foo>")))
167+
(is (= '[["cmd"] " FILE" (:file)] (parse-arg-names "cmd FILE")))
168+
(is (= '[["cmd"] " <file>" (:file)] (parse-arg-names "cmd <file>"))))
169+
170+
(mapcat build-flagmap-entries
171+
(coerce-to-pairs ["-i, --input ARG" {:description "foo"}]))
172+
(parse-flagstrs ["-i, --input, --[no-]foo ARG" {:description "foo"}])
173+
(parse-flagstrs ["-i, --input, --[no-]foo=<arg>" {:description "foo"}])
174+
(build-flagmap-entries ["-i, --input ARG" {:description "foo"}])
175+
176+
177+
;; (let [cmdspec
178+
;; {:commands {"run" {:command (show-args "run")
179+
;; :description "Do something"}
180+
;; "widget" {:description "Work with widgets"
181+
;; :commands
182+
;; ["ls" {:description "List widgets"
183+
;; :command (show-args "widget ls")}
184+
;; "add" {:description "Add widget"
185+
;; :command (show-args "widget add")}]}}
186+
;; :flags ["-i,--input FILE" {:desc "Specify input file"
187+
;; :key "XXX"}
188+
;; "--output FILE" "Specify output file"]}]
189+
;; (split-flags
190+
;; (assoc cmdspec :flagspecs (parse-flagstrs (:flags cmdspec)))
191+
;; ["widget" "ls" "--help" "--input" "INPUT" "--output" "OUTPUT"]))
192+
193+
;; (dispatch
194+
;; {:commands {"run" {:command (show-args "run")
195+
;; :description "Do something"}
196+
;; "widget" {:description "Work with widgets"
197+
;; :commands
198+
;; ["ls" {:description "List widgets"
199+
;; :command (show-args "widget ls")
200+
;; :help "List widgets in the order they exist."}
201+
;; "add" {:description "Add widget"
202+
;; :command (show-args "widget add")}]}}
203+
;; :flags ["-i,--input FILE" {:desc "Specify input file"}
204+
;; "--output FILE" "Specify output file"]}
205+
;; ["widget" "ls" "--help"])
206+
207+
;; (cli/dispatch
208+
;; {:commands
209+
;; ["run"
210+
;; {:description "Run the thing"
211+
;; :command (fn [args flags]
212+
;; ,,,)}
213+
214+
;; "widgets"
215+
;; {:description "Work with widgets"
216+
;; :subcommands
217+
;; ["ls"
218+
;; {:description "List widgets"
219+
;; :command (fn [args flags] ,,,)}
220+
;; "create NAME"
221+
;; {:description "Create a new widget"
222+
;; :command (fn [args flags] ,,,)}
223+
;; "delete ID"
224+
;; {:description "Delete the widget with the given ID"
225+
;; :command (fn [args flags] ,,,)}]}]
226+
227+
;; :flags
228+
;; ["-v,--verbose" {:description "Increase verbosity"}]})

src/lambdaisland/cli.clj

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,12 @@
122122
(for [[_ dash no flag] flags
123123
negative? (if no [true false] [false])]
124124
(cond-> {:flag (str dash (if negative? "no-") flag)
125-
:short? (= dash "-")
126-
:args args
127125
:key key
128126
:argcnt argcnt}
127+
(= dash "-")
128+
(assoc :short? true)
129+
(seq args)
130+
(assoc :args args)
129131
no
130132
(assoc :value (not negative?))
131133
:->
@@ -147,27 +149,23 @@
147149
[cmd (assoc v :argnames argnames)])))
148150
m)))
149151

150-
(parse-arg-names "ls AB")
151-
;; => [["foo"] " ABC" (:abc)]
152-
153152
(defn dispatch
154153
([cmdspec]
155154
(dispatch cmdspec *command-line-args*))
156155
([cmdspec cli-args]
157156
(let [[pos-args flags] (split-flags (assoc cmdspec :flagspecs (parse-flagstrs (:flags cmdspec))) cli-args)]
158157
(dispatch cmdspec pos-args flags)))
159158
([{:keys [commands flags name] :as cmdspec} pos-args opts]
160-
(let [[cmd & pos-args] pos-args
161-
cmd (first (str/split cmd #"[ =]"))
162-
_ (def xxx cmd)
163-
opts (update opts ::command (fnil conj []) cmd)
164-
program-name (or (:name cmdspec) "cli")
165-
command-map (prepare-cmdmap commands)
166-
_ (def yyy command-map)
167-
command-vec (if (vector? commands) commands (into [] cat commands))
168-
{command :command
159+
(let [[cmd & pos-args] pos-args
160+
pos-args (vec pos-args)
161+
cmd (when cmd (first (str/split cmd #"[ =]")))
162+
opts (update opts ::command (fnil conj []) cmd)
163+
program-name (or (:name cmdspec) "cli")
164+
command-map (prepare-cmdmap commands)
165+
command-vec (if (vector? commands) commands (into [] cat commands))
166+
{command :command
169167
subcommands :commands
170-
argnames :argnames} (get command-map cmd)]
168+
argnames :argnames} (get command-map cmd)]
171169
(cond
172170
(or (nil? cmd)
173171
(and (nil? command)

0 commit comments

Comments
 (0)