Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
ba0fa7dc5b | |||
4116771c98 | |||
7192a0d12c | |||
5b3b72e127 | |||
99ce77695c | |||
e55dc7556d | |||
14d25abc35 | |||
10ba753ee3 |
16
README.md
16
README.md
@@ -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 @@ You’ll 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 it’s the string `false`.
|
||||||
|
|
||||||
Once you’re done building your flag structure, you can run `CLI.parse`. It
|
Once you’re 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/>
|
||||||
|
|
||||||
|
367
cli.carp
367
cli.carp
@@ -3,63 +3,25 @@
|
|||||||
(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>)
|
|
||||||
```
|
|
||||||
|
|
||||||
You’ll 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 you’re 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 `Maybe`s, since they might be
|
|
||||||
optional arguments.")
|
|
||||||
(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 [])
|
||||||
)
|
)
|
||||||
|
|
||||||
(defmodule Type
|
(defmodule Type
|
||||||
(defn = [a b]
|
(defn = [a b]
|
||||||
(match @a
|
(match @a
|
||||||
|
(None)
|
||||||
|
(match @b
|
||||||
|
(None) true
|
||||||
|
_ false)
|
||||||
(Integer i)
|
(Integer i)
|
||||||
(match @b
|
(match @b
|
||||||
(Integer j) (= i j)
|
(Integer j) (= i j)
|
||||||
@@ -72,18 +34,23 @@ optional arguments.")
|
|||||||
(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
|
||||||
@@ -100,6 +67,11 @@ optional arguments.")
|
|||||||
(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)
|
||||||
@@ -109,6 +81,9 @@ optional arguments.")
|
|||||||
(match x
|
(match x
|
||||||
(Floating d) d
|
(Floating d) d
|
||||||
_ 0.0))
|
_ 0.0))
|
||||||
|
|
||||||
|
(defn zero [] (None))
|
||||||
|
(implements zero CLI.Type.zero)
|
||||||
)
|
)
|
||||||
|
|
||||||
(hidden Tag)
|
(hidden Tag)
|
||||||
@@ -117,42 +92,65 @@ optional arguments.")
|
|||||||
(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. It’s a (Pair (Pair <long> <short>) (<tag> <value>))
|
; this is pretty brutal. It’s 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
|
||||||
@@ -161,7 +159,7 @@ optional arguments.")
|
|||||||
(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)))))
|
||||||
@@ -170,9 +168,11 @@ optional arguments.")
|
|||||||
(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)
|
||||||
@@ -180,9 +180,11 @@ optional arguments.")
|
|||||||
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
|
||||||
@@ -191,9 +193,11 @@ optional arguments.")
|
|||||||
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
|
||||||
@@ -202,23 +206,39 @@ optional arguments.")
|
|||||||
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))
|
||||||
@@ -227,8 +247,11 @@ optional arguments.")
|
|||||||
(break)))))))
|
(break)))))))
|
||||||
|
|
||||||
(defn to-map [m]
|
(defn to-map [m]
|
||||||
(Array.reduce
|
(reduce
|
||||||
&(fn [a v] (Map.put a (Pair.a (Pair.a v)) (Pair.b (Pair.b v))))
|
&(fn [a v]
|
||||||
|
(match @(Pair.b (Pair.b v))
|
||||||
|
(Maybe.Just e) (Map.put a (Pair.a (Pair.a v)) &e)
|
||||||
|
(Maybe.Nothing) a))
|
||||||
{}
|
{}
|
||||||
(values m)))
|
(values m)))
|
||||||
)
|
)
|
||||||
@@ -243,24 +266,27 @@ optional arguments.")
|
|||||||
|
|
||||||
(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]
|
||||||
@@ -279,7 +305,7 @@ optional arguments.")
|
|||||||
(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))))
|
||||||
|
|
||||||
@@ -288,20 +314,22 @@ optional arguments.")
|
|||||||
(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`.
|
||||||
@@ -316,21 +344,32 @@ mesage is empty, `--help` was requested. If you don’t 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))
|
||||||
@@ -341,26 +380,27 @@ mesage is empty, `--help` was requested. If you don’t 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))))
|
||||||
@@ -370,12 +410,65 @@ mesage is empty, `--help` was requested. If you don’t 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>)
|
||||||
|
```
|
||||||
|
|
||||||
|
You’ll 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 it’s the string `false`.
|
||||||
|
|
||||||
|
Once you’re 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.")
|
||||||
|
@@ -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 "https://veitheller.de/git/carpentry/cli@0.0.2")
|
<pre><code class="language-clojure">(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!")
|
||||||
@@ -42,13 +42,14 @@
|
|||||||
(Result.Error msg) (do (IO.errorln &msg) (CLI.usage &p)))))
|
(Result.Error msg) (do (IO.errorln &msg) (CLI.usage &p)))))
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<h2>Installation</h2>
|
<h2>Installation</h2>
|
||||||
<pre><code class="language-clojure">(load "https://veitheller.de/git/carpentry/cli@0.0.2")
|
<pre><code class="language-clojure">(load "https://veitheller.de/git/carpentry/cli@0.0.7")
|
||||||
</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 <long> <short> <description> <required?>)
|
<pre><code class="language-clojure">(CLI.int <long> <short> <description> <required?>)
|
||||||
; or
|
; or
|
||||||
(CLI.int <long> <short> <description> <required?> <default>)
|
(CLI.int <long> <short> <description> <required?> <default>)
|
||||||
@@ -58,13 +59,34 @@ respectively. Their structure is always the same:</p>
|
|||||||
<p>You’ll have to set a default if you want to specify options, although you can
|
<p>You’ll 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 it’s the string <code>false</code>.</p>
|
||||||
<p>Once you’re done building your flag structure, you can run <code>CLI.parse</code>. It
|
<p>Once you’re 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
|
||||||
the long flag name to the value (the values are <code>Maybe</code>s, since they might be
|
the long flag name to the value. The values are not in the map if they are
|
||||||
optional arguments.</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 @@ optional arguments.</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 @@ optional arguments.</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 @@ optional arguments.</p>
|
|||||||
defn
|
defn
|
||||||
</div>
|
</div>
|
||||||
<p class="sig">
|
<p class="sig">
|
||||||
(λ [(Ref Parser)] (Result (Map String (Maybe 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 don’t 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)
|
||||||
|
@@ -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)))))
|
||||||
|
Reference in New Issue
Block a user