release 0.0.7

This commit is contained in:
2020-02-12 16:31:01 +01:00
parent 5b3b72e127
commit 7192a0d12c
4 changed files with 239 additions and 123 deletions

View File

@@ -18,15 +18,16 @@ A simple CLI library for Carp.
## Installation ## Installation
```clojure ```clojure
(load "https://veitheller.de/git/carpentry/cli@0.0.6") (load "https://veitheller.de/git/carpentry/cli@0.0.7")
``` ```
## Usage ## Usage
`CLI` should be built using combinators, as in the example above. It has, as of `CLI` should be built using combinators, as in the example above. It has, as of
now, three option types: integrals (longs), floating point numbers (doubles), now, three option types: integrals (longs), floating point numbers (doubles),
and strings. They can be built using `CLI.int`, `CLI.float`, and `CLI.str`, and strings. They can be built using `CLI.int`, `CLI.float`, `CLI.bool`, and
respectively. Their structure is always the same: `CLI.str`, respectively. Their structure is always the same, except for
booleans:
```clojure ```clojure
(CLI.int <long> <short> <description> <required?>) (CLI.int <long> <short> <description> <required?>)
@@ -40,6 +41,9 @@ Youll have to set a default if you want to specify options, although you can
set it to `(Maybe.Nothing)` if you want to make sure that it has to be set set it to `(Maybe.Nothing)` if you want to make sure that it has to be set
manually. manually.
Booleans neither take defaults nor options. If a boolean flag receives a value,
it will be read as true unless its the string `false`.
Once youre done building your flag structure, you can run `CLI.parse`. It Once youre done building your flag structure, you can run `CLI.parse`. It
will not abort the program on error, instead it will tell you what went wrong will not abort the program on error, instead it will tell you what went wrong
in a `Result.Error`. If it succeeds, the `Result.Success` contains a `Map` from in a `Result.Error`. If it succeeds, the `Result.Success` contains a `Map` from

229
cli.carp
View File

@@ -3,58 +3,15 @@
(defn zero [] (Maybe.Nothing)) (defn zero [] (Maybe.Nothing))
) )
(doc CLI "is a simple CLI library for Carp.
```clojure
(load \"https://veitheller.de/git/carpentry/cli@0.0.6\")
(defn main []
(let [p (=> (CLI.new @\"My super cool tool!\")
(CLI.add &(CLI.int \"flag\" \"f\" \"my flag\" true))
(CLI.add &(CLI.str \"thing\" \"t\" \"my thing\" false @\"hi\" &[@\"a\" @\"b\" @\"hi\"])))]
(match (CLI.parse &p)
(Result.Success flags)
(println* &(str &(Map.get &flags \"flag\")) \" \" &(str &(Map.get &flags \"thing\")))
(Result.Error msg) (do (IO.errorln &msg) (CLI.usage &p)))))
```
## Installation
```clojure
(load \"https://veitheller.de/git/carpentry/cli@0.0.6\")
```
## Usage
`CLI` should be built using combinators, as in the example above. It has, as of
now, three option types: integrals (longs), floating point numbers (doubles),
and strings. They can be built using `CLI.int`, `CLI.float`, and `CLI.str`,
respectively. Their structure is always the same:
```clojure
(CLI.int <long> <short> <description> <required?>)
; or
(CLI.int <long> <short> <description> <required?> <default>)
; or
(CLI.int <long> <short> <description> <required?> <default> <options-array>)
```
Youll have to set a default if you want to specify options, although you can
set it to `(Maybe.Nothing)` if you want to make sure that it has to be set
manually.
Once youre done building your flag structure, you can run `CLI.parse`. It
will not abort the program on error, instead it will tell you what went wrong
in a `Result.Error`. If it succeeds, the `Result.Success` contains a `Map` from
the long flag name to the value. The values are not in the map if they are
unset.")
(defmodule CLI (defmodule CLI
(use Array)
(hidden Type) (hidden Type)
(private Type) (private Type)
(deftype Type (deftype Type
(Integer [Long]) (Integer [Long])
(Floating [Double]) (Floating [Double])
(Str [String]) (Str [String])
(Boolean [Bool])
(None []) (None [])
) )
@@ -82,13 +39,14 @@ unset.")
(match @t (match @t
(Integer i) (Long.format s i) (Integer i) (Long.format s i)
(Floating f) (Double.format s f) (Floating f) (Double.format s f)
(Str s2) (String.format s &s2))) (Str s2) (format s &s2)))
(defn str [t] (defn str [t]
(match @t (match @t
(Integer i) (str i) (Integer i) (str i)
(Floating f) (str f) (Floating f) (str f)
(Str s) (str s) (Str s) (str s)
(Boolean b) (str b)
(None) @"none")) (None) @"none"))
(defn to-int [x] (defn to-int [x]
@@ -106,6 +64,11 @@ unset.")
(Str s) s (Str s) s
_ @"")) _ @""))
(defn to-bool [x]
(match x
(Boolean v) v
_ false))
(defn to-float [x] (defn to-float [x]
(match x (match x
(Floating d) (Double.to-float d) (Floating d) (Double.to-float d)
@@ -125,6 +88,7 @@ unset.")
(Integer []) (Integer [])
(Floating []) (Floating [])
(Str []) (Str [])
(Boolean [])
) )
(defmodule Tag (defmodule Tag
@@ -132,7 +96,27 @@ unset.")
(match t (match t
(Integer) (CLI.Type.Integer (Long.from-string s)) (Integer) (CLI.Type.Integer (Long.from-string s))
(Floating) (CLI.Type.Floating (Double.from-string s)) (Floating) (CLI.Type.Floating (Double.from-string s))
(Str) (CLI.Type.Str @s))) (Str) (CLI.Type.Str @s)
(Boolean) (CLI.Type.Boolean (/= s "false"))))
(defn = [a b]
(match @a
(Integer)
(match @b
(Integer) true
_ false)
(Floating)
(match @b
(Floating) true
_ false)
(Str)
(match @b
(Str) true
_ false)
(Boolean)
(match @b
(Boolean) true
_ false)))
) )
(doc Option "is the option type. To construct an `Option`, please use (doc Option "is the option type. To construct an `Option`, please use
@@ -154,22 +138,24 @@ unset.")
options (Array Option) options (Array Option)
]) ])
(private CmdMap)
(hidden CmdMap)
; this is pretty brutal. Its a (Pair (Pair <long> <short>) (<tag> <value>)) ; this is pretty brutal. Its a (Pair (Pair <long> <short>) (<tag> <value>))
; we need to make our own map because long or short might match and both are ; we need to make our own map because long or short might match and both are
; equivalent. This means a lot of manual work. Sorry about that. ; equivalent. This means a lot of manual work. Sorry about that.
(private CmdMap)
(hidden CmdMap)
(deftype CmdMap [ (deftype CmdMap [
values (Array (Pair (Pair String String) (Pair Tag (Maybe Type)))) values (Array (Pair (Pair String String) (Pair Tag (Maybe Type))))
]) ])
(defmodule CmdMap (defmodule CmdMap
(use Array)
(defn new [] (init [])) (defn new [] (init []))
(defn put [m o v] (defn put [m o v]
(update-values m (update-values m
&(fn [vs] &(fn [vs]
(Array.push-back vs (push-back vs
(Pair.init (Pair.init
(Pair.init @(CLI.Option.long o) @(CLI.Option.short o)) (Pair.init @(CLI.Option.long o) @(CLI.Option.short o))
@v))))) @v)))))
@@ -178,9 +164,11 @@ unset.")
(put m o &(Pair.init-from-refs (CLI.Option.type- o) (CLI.Option.default o)))) (put m o &(Pair.init-from-refs (CLI.Option.type- o) (CLI.Option.default o))))
(defn contains? [m s] (defn contains? [m s]
(let-do [found false] (let-do [found false
(foreach [e (values m)] vs (values m)]
(let [k (Pair.a e)] (for [i 0 (length vs)]
(let [e (unsafe-nth vs i)
k (Pair.a e)]
(when (or (= (Pair.a k) s) (= (Pair.b k) s)) (when (or (= (Pair.a k) s) (= (Pair.b k) s))
(do (do
(set! found true) (set! found true)
@@ -188,9 +176,11 @@ unset.")
found)) found))
(defn set? [m s] (defn set? [m s]
(let-do [found false] (let-do [found false
(foreach [e (values m)] vs (values m)]
(let [k (Pair.a e) (for [i 0 (length vs)]
(let [e (unsafe-nth vs i)
k (Pair.a e)
v (Pair.b (Pair.b e))] v (Pair.b (Pair.b e))]
(when (or (= (Pair.a k) s) (= (Pair.b k) s)) (when (or (= (Pair.a k) s) (= (Pair.b k) s))
(do (do
@@ -199,9 +189,11 @@ unset.")
found)) found))
(defn get [m s] (defn get [m s]
(let-do [res (CLI.Type.Str @"")] (let-do [res (CLI.Type.Str @"")
(foreach [e (values m)] vs (values m)]
(let [k (Pair.a e) (for [i 0 (length vs)]
(let [e (unsafe-nth vs i)
k (Pair.a e)
v (Pair.b (Pair.b e))] v (Pair.b (Pair.b e))]
(when (or (= (Pair.a k) s) (= (Pair.b k) s)) (when (or (= (Pair.a k) s) (= (Pair.b k) s))
(do (do
@@ -210,23 +202,39 @@ unset.")
res)) res))
(defn in? [m s vs] (defn in? [m s vs]
(let-do [found true] (let-do [found true
(foreach [e (values m)] vals (values m)]
(let [k (Pair.a e) (for [i 0 (length vals)]
(let [e (unsafe-nth vals i)
k (Pair.a e)
v (Pair.b (Pair.b e))] v (Pair.b (Pair.b e))]
(when (or (= (Pair.a k) s) (= (Pair.b k) s)) (when (or (= (Pair.a k) s) (= (Pair.b k) s))
(match @v (match @v
(Maybe.Just value) (Maybe.Just value)
(do (do
(set! found (Array.contains? vs &value)) (set! found (contains? vs &value))
(break)) (break))
(Maybe.Nothing) (break))))) (Maybe.Nothing) (break)))))
found)) found))
(defn type? [m s t]
(let-do [found false
vs (values m)]
(for [i 0 (length vs)]
(let [e (unsafe-nth vs i)
k (Pair.a e)
v (Pair.a (Pair.b e))]
(when (and (or (= (Pair.a k) s) (= (Pair.b k) s))
(= v t))
(do
(set! found true)
(break)))))
found))
(defn put! [m s v] (defn put! [m s v]
(let [vs (values m)] (let [vs (values m)]
(for [i 0 (Array.length vs)] (for [i 0 (length vs)]
(let [p (Array.unsafe-nth vs i) (let [p (unsafe-nth vs i)
k (Pair.a p) k (Pair.a p)
vp (Pair.b p)] vp (Pair.b p)]
(when (or (= (Pair.a k) s) (= (Pair.b k) s)) (when (or (= (Pair.a k) s) (= (Pair.b k) s))
@@ -235,7 +243,7 @@ unset.")
(break))))))) (break)))))))
(defn to-map [m] (defn to-map [m]
(Array.reduce (reduce
&(fn [a v] &(fn [a v]
(match @(Pair.b (Pair.b v)) (match @(Pair.b (Pair.b v))
(Maybe.Just e) (Map.put a (Pair.a (Pair.a v)) &e) (Maybe.Just e) (Map.put a (Pair.a (Pair.a v)) &e)
@@ -254,7 +262,7 @@ unset.")
(doc add "adds an `Option` `opt` to the `Parser` `p`.") (doc add "adds an `Option` `opt` to the `Parser` `p`.")
(defn add [p opt] (defn add [p opt]
(Parser.update-options p &(fn [options] (Array.push-back options @opt)))) (Parser.update-options p &(fn [options] (push-back options @opt))))
(hidden option-) (hidden option-)
(private option-) (private option-)
@@ -273,6 +281,10 @@ unset.")
(list 'Maybe.Just (list 'Maybe.Just
(list 'Array.copy-map '(ref (fn [e] (to-cli-type @e))) (cadr default-options))))))) (list 'Array.copy-map '(ref (fn [e] (to-cli-type @e))) (cadr default-options)))))))
(doc bool "creates a boolean option.")
(defmacro bool [long short description]
(CLI.option- 'CLI.Tag.Boolean long short description false []))
(doc str "creates a string option.") (doc str "creates a string option.")
(defmacro str [long short description required :rest default-options] (defmacro str [long short description required :rest default-options]
(CLI.option- 'CLI.Tag.Str long short description required default-options)) (CLI.option- 'CLI.Tag.Str long short description required default-options))
@@ -290,7 +302,7 @@ unset.")
(defn options-str [p] (defn options-str [p]
(join (join
" " " "
&(Array.copy-map &(copy-map
&(fn [o] (fmt "[-%s | --%s]" (Option.short o) (Option.long o))) &(fn [o] (fmt "[-%s | --%s]" (Option.short o) (Option.long o)))
(Parser.options p)))) (Parser.options p))))
@@ -300,7 +312,8 @@ unset.")
(IO.println (IO.println
&(fmt "usage: %s %s\n%s\nOptions:" &(fmt "usage: %s %s\n%s\nOptions:"
(System.get-arg 0) &(options-str p) (Parser.description p))) (System.get-arg 0) &(options-str p) (Parser.description p)))
(foreach [arg (Parser.options p)] (for [i 0 (length (Parser.options p))]
(let [arg (unsafe-nth (Parser.options p) i)]
(do (do
(IO.print (IO.print
&(fmt " --%s|-%s: %s" &(fmt " --%s|-%s: %s"
@@ -310,9 +323,9 @@ unset.")
(IO.print &(fmt " (default: %s)" &(str &(Maybe.unsafe-from @(Option.default arg)))))) (IO.print &(fmt " (default: %s)" &(str &(Maybe.unsafe-from @(Option.default arg))))))
(match @(Option.options arg) (match @(Option.options arg)
(Maybe.Just o) (Maybe.Just o)
(IO.print &(fmt " (options: %s)" &(join ", " &(Array.copy-map &str &o)))) (IO.print &(fmt " (options: %s)" &(join ", " &(copy-map &str &o))))
(Maybe.Nothing) ()) (Maybe.Nothing) ())
(IO.println ""))) (IO.println ""))))
(IO.println " --help|-h: print this help message and exit."))) (IO.println " --help|-h: print this help message and exit.")))
(doc parse "parses the arguments as specified by the parser `p`. (doc parse "parses the arguments as specified by the parser `p`.
@@ -330,22 +343,27 @@ mesage is empty, `--help` was requested. If you dont want to provide a
options (Parser.options p)] options (Parser.options p)]
(for [i 1 (System.get-args-len)] (for [i 1 (System.get-args-len)]
(let [x (System.get-arg i)] (let [x (System.get-arg i)]
(if (or (String.starts-with? x "--") (String.starts-with? x "-")) (if (or (starts-with? x "--") (starts-with? x "-"))
(let [flag (Pattern.substitute #"^\-\-?" x "" 1) (let [flag (Pattern.substitute #"^\-\-?" x "" 1)
splt (String.split-by &flag &[\=]) splt (split-by &flag &[\=])
k (if (> (Array.length &splt) 1) (Array.unsafe-nth &splt 0) &flag) k (if (> (length &splt) 1) (unsafe-nth &splt 0) &flag)
v (cond (> (Array.length &splt) 1) (Array.nth &splt 1) v (cond (> (length &splt) 1) (nth &splt 1)
(< i (System.get-args-len)) (< i (Int.dec (System.get-args-len)))
(do (set! i (Int.inc i)) (Maybe.Just @(System.get-arg i))) (do (set! i (Int.inc i)) (Maybe.Just @(System.get-arg i)))
(Maybe.Nothing))] (Maybe.Nothing))]
(cond (cond
(CmdMap.contains? &values k) (CmdMap.contains? &values k)
(if (CmdMap.type? &values k &(Tag.Boolean))
(do
(when (and (Maybe.just? &v) (> (length &splt) 1))
(set! i (Int.dec i)))
(CmdMap.put! &values k "true"))
(match v (match v
(Maybe.Just val) (CmdMap.put! &values k &val) (Maybe.Just val) (CmdMap.put! &values k &val)
(Maybe.Nothing) (Maybe.Nothing)
(do (do
(set! res (Result.Error (fmt "No value for: %s" &flag))) (set! res (Result.Error (fmt "No value for: %s" &flag)))
(break))) (break))))
(or (= k "help") (= k "h")) (or (= k "help") (= k "h"))
(do (do
(set! res (Result.Error @"")) (set! res (Result.Error @""))
@@ -357,7 +375,8 @@ mesage is empty, `--help` was requested. If you dont want to provide a
(set! res (Result.Error (fmt "Unexpected argument: %s" x))) (set! res (Result.Error (fmt "Unexpected argument: %s" x)))
(break))))) (break)))))
(when (Result.success? &res) (when (Result.success? &res)
(foreach [o options] (for [i 0 (length options)]
(let [o (unsafe-nth options i)]
(cond (cond
(and @(Option.required? o) (and @(Option.required? o)
(not (CmdMap.set? &values (Option.long o)))) (not (CmdMap.set? &values (Option.long o))))
@@ -374,9 +393,9 @@ mesage is empty, `--help` was requested. If you dont want to provide a
"Option %s received an invalid option %s (Options are %s)" "Option %s received an invalid option %s (Options are %s)"
(Option.long o) (Option.long o)
&(CmdMap.get &values (Option.long o)) &(CmdMap.get &values (Option.long o))
&(join ", " &(Array.copy-map &str &opts))))) &(join ", " &(copy-map &str &opts)))))
(break)))) (break))))
()))) ()))))
(match res (match res
(Result.Success _) (Result.Success (CmdMap.to-map &values)) (Result.Success _) (Result.Success (CmdMap.to-map &values))
(Result.Error x) (Result.Error x)))) (Result.Error x) (Result.Error x))))
@@ -395,3 +414,53 @@ mesage is empty, `--help` was requested. If you dont want to provide a
(defmodule Long (defmodule Long
(defn to-cli-type [l] (CLI.Type.Integer l)) (defn to-cli-type [l] (CLI.Type.Integer l))
) )
(doc CLI "is a simple CLI library for Carp.
```clojure
(load \"https://veitheller.de/git/carpentry/cli@0.0.6\")
(defn main []
(let [p (=> (CLI.new @\"My super cool tool!\")
(CLI.add &(CLI.int \"flag\" \"f\" \"my flag\" true))
(CLI.add &(CLI.str \"thing\" \"t\" \"my thing\" false @\"hi\" &[@\"a\" @\"b\" @\"hi\"])))]
(match (CLI.parse &p)
(Result.Success flags)
(println* &(str &(Map.get &flags \"flag\")) \" \" &(str &(Map.get &flags \"thing\")))
(Result.Error msg) (do (IO.errorln &msg) (CLI.usage &p)))))
```
## Installation
```clojure
(load \"https://veitheller.de/git/carpentry/cli@0.0.7\")
```
## Usage
`CLI` should be built using combinators, as in the example above. It has, as of
now, three option types: integrals (longs), floating point numbers (doubles),
and strings. They can be built using `CLI.int`, `CLI.float`, `CLI.bool`, and
`CLI.str`, respectively. Their structure is always the same, except for
booleans:
```clojure
(CLI.int <long> <short> <description> <required?>)
; or
(CLI.int <long> <short> <description> <required?> <default>)
; or
(CLI.int <long> <short> <description> <required?> <default> <options-array>)
```
Youll have to set a default if you want to specify options, although you can
set it to `(Maybe.Nothing)` if you want to make sure that it has to be set
manually.
Booleans neither take defaults nor options. If a boolean flag receives a value,
it will be read as true unless its the string `false`.
Once youre done building your flag structure, you can run `CLI.parse`. It
will not abort the program on error, instead it will tell you what went wrong
in a `Result.Error`. If it succeeds, the `Result.Success` contains a `Map` from
the long flag name to the value. The values are not in the map if they are
unset.")

View File

@@ -42,13 +42,14 @@
(Result.Error msg) (do (IO.errorln &amp;msg) (CLI.usage &amp;p))))) (Result.Error msg) (do (IO.errorln &amp;msg) (CLI.usage &amp;p)))))
</code></pre> </code></pre>
<h2>Installation</h2> <h2>Installation</h2>
<pre><code class="language-clojure">(load &quot;https://veitheller.de/git/carpentry/cli@0.0.6&quot;) <pre><code class="language-clojure">(load &quot;https://veitheller.de/git/carpentry/cli@0.0.7&quot;)
</code></pre> </code></pre>
<h2>Usage</h2> <h2>Usage</h2>
<p><code>CLI</code> should be built using combinators, as in the example above. It has, as of <p><code>CLI</code> should be built using combinators, as in the example above. It has, as of
now, three option types: integrals (longs), floating point numbers (doubles), now, three option types: integrals (longs), floating point numbers (doubles),
and strings. They can be built using <code>CLI.int</code>, <code>CLI.float</code>, and <code>CLI.str</code>, and strings. They can be built using <code>CLI.int</code>, <code>CLI.float</code>, <code>CLI.bool</code>, and
respectively. Their structure is always the same:</p> <code>CLI.str</code>, respectively. Their structure is always the same, except for
booleans:</p>
<pre><code class="language-clojure">(CLI.int &lt;long&gt; &lt;short&gt; &lt;description&gt; &lt;required?&gt;) <pre><code class="language-clojure">(CLI.int &lt;long&gt; &lt;short&gt; &lt;description&gt; &lt;required?&gt;)
; or ; or
(CLI.int &lt;long&gt; &lt;short&gt; &lt;description&gt; &lt;required?&gt; &lt;default&gt;) (CLI.int &lt;long&gt; &lt;short&gt; &lt;description&gt; &lt;required?&gt; &lt;default&gt;)
@@ -58,6 +59,8 @@ respectively. Their structure is always the same:</p>
<p>Youll have to set a default if you want to specify options, although you can <p>Youll have to set a default if you want to specify options, although you can
set it to <code>(Maybe.Nothing)</code> if you want to make sure that it has to be set set it to <code>(Maybe.Nothing)</code> if you want to make sure that it has to be set
manually.</p> manually.</p>
<p>Booleans neither take defaults nor options. If a boolean flag receives a value,
it will be read as true unless its the string <code>false</code>.</p>
<p>Once youre done building your flag structure, you can run <code>CLI.parse</code>. It <p>Once youre done building your flag structure, you can run <code>CLI.parse</code>. It
will not abort the program on error, instead it will tell you what went wrong will not abort the program on error, instead it will tell you what went wrong
in a <code>Result.Error</code>. If it succeeds, the <code>Result.Success</code> contains a <code>Map</code> from in a <code>Result.Error</code>. If it succeeds, the <code>Result.Success</code> contains a <code>Map</code> from
@@ -65,6 +68,25 @@ the long flag name to the value. The values are not in the map if they are
unset.</p> unset.</p>
</div> </div>
<div class="binder">
<a class="anchor" href="#CmdMap">
<h3 id="CmdMap">
CmdMap
</h3>
</a>
<div class="description">
module
</div>
<p class="sig">
Module
</p>
<span>
</span>
<p class="doc">
</p>
</div>
<div class="binder"> <div class="binder">
<a class="anchor" href="#Option"> <a class="anchor" href="#Option">
<h3 id="Option"> <h3 id="Option">
@@ -117,7 +139,7 @@ unset.</p>
defn defn
</div> </div>
<p class="sig"> <p class="sig">
(λ [Parser, (Ref Option)] Parser) (λ [Parser, (Ref Option a)] Parser)
</p> </p>
<pre class="args"> <pre class="args">
(add p opt) (add p opt)
@@ -127,6 +149,26 @@ unset.</p>
</p> </p>
</div> </div>
<div class="binder">
<a class="anchor" href="#bool">
<h3 id="bool">
bool
</h3>
</a>
<div class="description">
macro
</div>
<p class="sig">
Macro
</p>
<pre class="args">
(bool long short description)
</pre>
<p class="doc">
<p>creates a boolean option.</p>
</p>
</div>
<div class="binder"> <div class="binder">
<a class="anchor" href="#float"> <a class="anchor" href="#float">
<h3 id="float"> <h3 id="float">
@@ -197,7 +239,7 @@ unset.</p>
defn defn
</div> </div>
<p class="sig"> <p class="sig">
(λ [(Ref Parser)] (Result (Map String Type) String)) (λ [(Ref Parser a)] (Result (Map String Type) String))
</p> </p>
<pre class="args"> <pre class="args">
(parse p) (parse p)
@@ -243,7 +285,7 @@ mesage is empty, <code>--help</code> was requested. If you dont want to provi
defn defn
</div> </div>
<p class="sig"> <p class="sig">
(λ [(Ref Parser)] ()) (λ [(Ref Parser a)] ())
</p> </p>
<pre class="args"> <pre class="args">
(usage p) (usage p)

View File

@@ -4,10 +4,11 @@
(let [p (=> (CLI.new @"My super cool tool!") (let [p (=> (CLI.new @"My super cool tool!")
(CLI.add &(CLI.int "flag" "f" "my flag" true)) (CLI.add &(CLI.int "flag" "f" "my flag" true))
(CLI.add &(CLI.str "thing" "t" "my thing" false @"hi" &[@"a" @"b" @"hi"])) (CLI.add &(CLI.str "thing" "t" "my thing" false @"hi" &[@"a" @"b" @"hi"]))
(CLI.add &(CLI.str "other" "o" "my thing" false)))] (CLI.add &(CLI.bool "boolean" "b" "my boolean"))
(CLI.add &(CLI.str "other" "o" "my other thing" false)))]
(match (CLI.parse &p) (match (CLI.parse &p)
(Result.Success flags) (Result.Success flags)
(println* (println*
&(str &(Map.get &flags "flag")) " " &(str &(Map.get &flags "thing")) &(str &(Map.get &flags "flag")) " " &(str &(Map.get &flags "thing"))
" " &(str &(Map.get &flags "other"))) " " &(str &(Map.get &flags "other")) " " &(str &(Map.get &flags "boolean")))
(Result.Error msg) (do (IO.errorln &msg) (CLI.usage &p))))) (Result.Error msg) (do (IO.errorln &msg) (CLI.usage &p)))))