7 Commits

Author SHA1 Message Date
ba0fa7dc5b invalid c 2021-01-19 14:05:40 +01:00
4116771c98 fix typo 2020-02-12 16:33:19 +01:00
7192a0d12c release 0.0.7 2020-02-12 16:31:01 +01:00
5b3b72e127 vbump 2020-01-31 22:30:42 +01:00
99ce77695c make = work as well 2020-01-31 22:26:37 +01:00
e55dc7556d version bump 2020-01-31 17:31:06 +01:00
14d25abc35 bugfix for printing none 2020-01-31 17:28:31 +01:00
4 changed files with 270 additions and 156 deletions

View File

@@ -3,7 +3,7 @@
A simple CLI library for Carp. A simple CLI library for Carp.
```clojure ```clojure
(load "https://veitheller.de/git/carpentry/cli@0.0.2") (load "https://veitheller.de/git/carpentry/cli@0.0.7")
(defn main [] (defn main []
(let [p (=> (CLI.new @"My super cool tool!") (let [p (=> (CLI.new @"My super cool tool!")
@@ -18,15 +18,16 @@ A simple CLI library for Carp.
## Installation ## Installation
```clojure ```clojure
(load "https://veitheller.de/git/carpentry/cli@0.0.2") (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,11 +41,14 @@ 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
the long flag name to the value (the values are `Maybe`s, since they might be the long flag name to the value. The values are not in the map if they are
optional arguments. unset.
<hr/> <hr/>

355
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.2\")
(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.2\")
```
## 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 [])
) )
@@ -77,18 +34,23 @@ unset.")
(match @b (match @b
(Str t) (= s t) (Str t) (= s t)
_ false))) _ false)))
(implements = CLI.Type.=)
(defn format [s t] (defn format [s t]
(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)))
(implements format CLI.Type.format)
(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"))
(implements str CLI.Type.str)
(defn to-int [x] (defn to-int [x]
(match x (match x
@@ -105,6 +67,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)
@@ -116,6 +83,7 @@ unset.")
_ 0.0)) _ 0.0))
(defn zero [] (None)) (defn zero [] (None))
(implements zero CLI.Type.zero)
) )
(hidden Tag) (hidden Tag)
@@ -124,42 +92,65 @@ unset.")
(Integer []) (Integer [])
(Floating []) (Floating [])
(Str []) (Str [])
(Boolean [])
) )
(defmodule Tag (defmodule Tag
(defn to-type [t s] (defn to-type [t s]
(match t (match t
(Integer) (CLI.Type.Integer (Long.from-string s)) (Integer) (CLI.Type.Integer (Maybe.unsafe-from (from-string s)))
(Floating) (CLI.Type.Floating (Double.from-string s)) (Floating) (CLI.Type.Floating (Maybe.unsafe-from (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)))
(implements = CLI.Tag.=)
) )
(doc Option "is the option type. To construct an `Option`, please use (doc Option "is the option type. To construct an `Option`, please use
[`int`](#int), [`float`](#float), or [`str`](#str).") [`int`](#int), [`float`](#float), or [`str`](#str).")
(deftype Option [ (deftype Option [
type- Tag type- CLI.Tag
long String long String
short String short String
description String description String
required? Bool required? Bool
default (Maybe Type) default (Maybe CLI.Type)
options (Maybe (Array Type)) options (Maybe (Array CLI.Type))
]) ])
(doc Parser "is the parser type. To construct a `Parser`, please use (doc Parser "is the parser type. To construct a `Parser`, please use
[`new`](#new).") [`new`](#new).")
(deftype Parser [ (deftype Parser [
description String description String
options (Array Option) options (Array CLI.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 CLI.Tag (Maybe CLI.Type))))
]) ])
(defmodule CmdMap (defmodule CmdMap
@@ -168,7 +159,7 @@ unset.")
(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)))))
@@ -177,9 +168,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)
@@ -187,9 +180,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
@@ -198,9 +193,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
@@ -209,23 +206,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))
@@ -234,7 +247,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)
@@ -253,24 +266,27 @@ 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-)
(defndynamic option- [t long short description required default-options] (defndynamic option- [t long short description required default-options]
(if (= (length default-options) 0) (if (= (length default-options) 0)
(list 'CLI.Option.init (list t) `(CLI.Option.init (%t)
(list 'copy long) (list 'copy short) (list 'copy description) (copy %long) (copy %short) (copy %description)
required '(Maybe.Nothing) '(Maybe.Nothing)) %required (Maybe.Nothing) (Maybe.Nothing))
(if (= (length default-options) 1) (if (= (length default-options) 1)
(list 'CLI.Option.init (list t) `(CLI.Option.init (%t)
(list 'copy long) (list 'copy short) (list 'copy description) (copy %long) (copy %short) (copy %description)
required (list 'Maybe.Just (list 'to-cli-type (car default-options))) '(Maybe.Nothing)) %required (Maybe.Just (to-cli-type %(car default-options))) (Maybe.Nothing))
(list 'CLI.Option.init (list t) `(CLI.Option.init (%t)
(list 'copy long) (list 'copy short) (list 'copy description) (copy %long) (copy %short) (copy %description)
required (list 'Maybe.Just (list 'to-cli-type (car default-options))) %required (Maybe.Just (to-cli-type %(car default-options)))
(list 'Maybe.Just (Maybe.Just
(list 'Array.copy-map '(ref (fn [e] (to-cli-type @e))) (cadr default-options))))))) (Array.copy-map &(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]
@@ -289,7 +305,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))))
@@ -298,20 +314,22 @@ unset.")
(do (do
(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))) (StaticArray.unsafe-nth &System.args 0)
(foreach [arg (Parser.options p)] &(options-str p) (Parser.description p)))
(do (for [i 0 (length (Parser.options p))]
(IO.print (let [arg (unsafe-nth (Parser.options p) i)]
&(fmt " --%s|-%s: %s" (do
(Option.long arg) (Option.short arg) (Option.description arg))) (IO.print
(when @(Option.required? arg) (IO.print " REQUIRED")) &(fmt " --%s|-%s: %s"
(when (Maybe.just? (Option.default arg)) (Option.long arg) (Option.short arg) (Option.description arg)))
(IO.print &(fmt " (default: %s)" &(str &(Maybe.unsafe-from @(Option.default arg)))))) (when @(Option.required? arg) (IO.print " REQUIRED"))
(match @(Option.options arg) (when (Maybe.just? (Option.default arg))
(Maybe.Just o) (IO.print &(fmt " (default: %s)" &(str &(Maybe.unsafe-from @(Option.default arg))))))
(IO.print &(fmt " (options: %s)" &(join ", " &(Array.copy-map &str &o)))) (match @(Option.options arg)
(Maybe.Nothing) ()) (Maybe.Just o)
(IO.println ""))) (IO.print &(fmt " (options: %s)" &(join ", " &(copy-map &str &o))))
(Maybe.Nothing) ())
(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`.
@@ -326,21 +344,32 @@ mesage is empty, `--help` was requested. If you dont want to provide a
(defn parse [p] (defn parse [p]
(let-do [values (Parser.values p) (let-do [values (Parser.values p)
res (Result.Success @p) res (Result.Success @p)
options (Parser.options p)] options (Parser.options p)
(for [i 1 (System.get-args-len)] len (StaticArray.length &System.args)]
(let [x (System.get-arg i)] (for [i 1 len]
(if (or (String.starts-with? x "--") (String.starts-with? x "-")) (let [x (StaticArray.unsafe-nth &System.args i)]
(let [flag (Pattern.substitute #"^\-\-?" x "" 1)] (if (or (starts-with? x "--") (starts-with? x "-"))
(let [flag (Pattern.substitute #"^\-\-?" x "" 1)
splt (split-by &flag &[\=])
k (if (> (length &splt) 1) (unsafe-nth &splt 0) &flag)
v (cond (> (length &splt) 1) (nth &splt 1)
(< i (Int.dec len))
(do (set! i (Int.inc i)) (Maybe.Just @(StaticArray.unsafe-nth &System.args i)))
(Maybe.Nothing))]
(cond (cond
(CmdMap.contains? &values &flag) (CmdMap.contains? &values k)
(do (if (CmdMap.type? &values k &(Tag.Boolean))
(set! i (Int.inc i)) (do
(if (< i (System.get-args-len)) (when (and (Maybe.just? &v) (> (length &splt) 1))
(CmdMap.put! &values &flag (System.get-arg i)) (set! i (Int.dec i)))
(do (CmdMap.put! &values k "true"))
(set! res (Result.Error (fmt "No value for: %s" &flag))) (match v
(break)))) (Maybe.Just val) (CmdMap.put! &values k &val)
(or (= &flag "help") (= &flag "h")) (Maybe.Nothing)
(do
(set! res (Result.Error (fmt "No value for: %s" &flag)))
(break))))
(or (= k "help") (= k "h"))
(do (do
(set! res (Result.Error @"")) (set! res (Result.Error @""))
(break)) (break))
@@ -351,26 +380,27 @@ 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)]
(cond (let [o (unsafe-nth options i)]
(and @(Option.required? o) (cond
(not (CmdMap.set? &values (Option.long o)))) (and @(Option.required? o)
(do (not (CmdMap.set? &values (Option.long o))))
(set! res (Result.Error (fmt "Required option missing: --%s" (Option.long o)))) (do
(break)) (set! res (Result.Error (fmt "Required option missing: --%s" (Option.long o))))
(Maybe.just? (Option.options o)) (break))
(let-do [opts (Maybe.unsafe-from @(Option.options o))] (Maybe.just? (Option.options o))
(when (not (CmdMap.in? &values (Option.long o) &opts)) (let-do [opts (Maybe.unsafe-from @(Option.options o))]
(do (when (not (CmdMap.in? &values (Option.long o) &opts))
(set! res (do
(Result.Error (set! res
(fmt (Result.Error
"Option %s received an invalid option %s (Options are %s)" (fmt
(Option.long o) "Option %s received an invalid option %s (Options are %s)"
&(CmdMap.get &values (Option.long o)) (Option.long o)
&(join ", " &(Array.copy-map &str &opts))))) &(CmdMap.get &values (Option.long o))
(break)))) &(join ", " &(copy-map &str &opts)))))
()))) (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))))
@@ -380,12 +410,65 @@ mesage is empty, `--help` was requested. If you dont want to provide a
(defmodule String (defmodule String
(defn to-cli-type [s] (CLI.Type.Str s)) (defn to-cli-type [s] (CLI.Type.Str s))
(implements to-cli-type String.to-cli-type)
) )
(defmodule Double (defmodule Double
(defn to-cli-type [f] (CLI.Type.Floating f)) (defn to-cli-type [f] (CLI.Type.Floating f))
(implements to-cli-type Double.to-cli-type)
) )
(defmodule Long (defmodule Long
(defn to-cli-type [l] (CLI.Type.Integer l)) (defn to-cli-type [l] (CLI.Type.Integer l))
(implements to-cli-type Long.to-cli-type)
) )
(doc CLI "is a simple CLI library for Carp.
```clojure
(load \"https://veitheller.de/git/carpentry/cli@0.0.7\")
(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

@@ -30,7 +30,7 @@
</h1> </h1>
<div class="module-description"> <div class="module-description">
<p>is a simple CLI library for Carp.</p> <p>is a simple CLI library for Carp.</p>
<pre><code class="language-clojure">(load &quot;https://veitheller.de/git/carpentry/cli@0.0.2&quot;) <pre><code class="language-clojure">(load &quot;https://veitheller.de/git/carpentry/cli@0.0.7&quot;)
(defn main [] (defn main []
(let [p (=&gt; (CLI.new @&quot;My super cool tool!&quot;) (let [p (=&gt; (CLI.new @&quot;My super cool tool!&quot;)
@@ -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.2&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
@@ -66,16 +69,16 @@ unset.</p>
</div> </div>
<div class="binder"> <div class="binder">
<a class="anchor" href="#(defdynamic CLI.*gensym-counter* 1001)"> <a class="anchor" href="#CmdMap">
<h3 id="(defdynamic CLI.*gensym-counter* 1001)"> <h3 id="CmdMap">
(defdynamic CLI.*gensym-counter* 1001) CmdMap
</h3> </h3>
</a> </a>
<div class="description"> <div class="description">
dynamic module
</div> </div>
<p class="sig"> <p class="sig">
Dynamic Module
</p> </p>
<span> <span>
@@ -136,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)
@@ -146,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">
@@ -216,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)
@@ -262,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

@@ -3,8 +3,12 @@
(defn main [] (defn main []
(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.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* &(str &(Map.get &flags "flag")) " " &(str &(Map.get &flags "thing"))) (println*
&(str &(Map.get &flags "flag")) " " &(str &(Map.get &flags "thing"))
" " &(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)))))