/
tools_cli.clj
71 lines (64 loc) · 2.72 KB
/
tools_cli.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
(ns fn2api.cli.tools-cli
(:require [clojure.spec.alpha :as s]
[spec-tools.core :as st]
[spec-tools.spec :as sp]
[clojure.tools.cli :refer [parse-opts summarize]]
[fn2api.lib.spec :refer [sub-specs]]
[fn2api.lib.keyword :refer [keyword-without-ns]]
[clojure.string :refer [upper-case]]
[clojure.pprint :refer [pprint]]))
(defn ->sub-specs-kw->spec [var-f]
(->> (sub-specs var-f)
(map #(hash-map (keyword-without-ns %)
(s/get-spec %)))
(apply merge)))
(defn ->arg-names
[var-f]
(->> var-f
meta
:arglists
first ;; for now we assume single arity
first
:keys))
(defn ->cli-options
[var-f]
(let [sub-specs-kw->description (->> (sub-specs var-f)
(map #(hash-map (keyword-without-ns %)
(st/spec-description (s/get-spec %))))
(apply merge))
arg-names (->arg-names var-f)]
(for [arg-name arg-names]
[(str "-" arg-name) ;; assume only 1 char; in future take it from spec
(str "--" arg-name " " (upper-case arg-name))
(get sub-specs-kw->description (keyword arg-name))
;; TODO take default from spec
])))
(defn usage
"Takes the input of parse-opts with :summary-fn identity"
[parsed]
(str "Usage:\n"
(summarize (:summary parsed))
"\n"))
(defn run-f! [var-f args]
(let [cli-options (concat [["-h" "--help"]]
(->cli-options var-f))
parsed (parse-opts args cli-options :summary-fn identity)
parsed+coerced (->> (map (fn [[arg-name arg-val]]
{arg-name (st/conform (get (->sub-specs-kw->spec var-f) arg-name)
arg-val
st/string-conforming)})
(filter (fn [[arg-name arg-val]]
(some #{arg-name} (map keyword (->arg-names var-f))))
(:options parsed)))
(apply merge))
invalid?->explain (st/explain-data (:args (s/get-spec var-f))
[(:options parsed)]
st/string-conforming)]
(pprint cli-options)
(pprint (dissoc parsed :summary))
(pprint parsed+coerced)
(if (get-in parsed [:options :help])
(usage parsed)
(if invalid?->explain
(pprint invalid?->explain)
((deref var-f) parsed+coerced)))))