-
Notifications
You must be signed in to change notification settings - Fork 1
/
reader.clj
124 lines (102 loc) · 2.91 KB
/
reader.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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
(ns enviable.reader
(:require [clojure.string :as str]))
(defn get-ns []
(try
(throw (Exception. ""))
(catch Exception e
(let [stack (->> e
Throwable->map
:trace
(map first)
(remove #(str/starts-with? % "clojure."))
(remove #(str/starts-with? % "enviable.reader")))]
(-> stack
first
str
(str/split #"\$")
first)))))
(defn var [var-name]
{::name var-name
::ns (get-ns)
::parser identity})
(defn env-var? [x]
(::name x))
(defn parse-with [var parser]
(assoc var ::parser parser))
(defn default-to [var default]
(assoc var ::default {::default-value default}))
(defn is-optional [var]
(default-to var nil))
(defn describe [var description]
(assoc var ::description description))
(defn read-result
([var input-val]
(read-result var input-val nil))
([var input-val parsed-val]
{::name (::name var)
::description (::description var)
::ns (::ns var)
::input input-val
::parsed parsed-val}))
(defn error [read-result]
{::error [read-result]})
(defn ok [read-result read-val]
{::ok [read-result]
::value read-val})
(defn error? [result]
(boolean (::error result)))
(defn fmap [v-or-error f & args]
(if (error? v-or-error)
v-or-error
(apply f v-or-error args)))
(defn lmap [v-or-error f & args]
(if (error? v-or-error)
(apply f v-or-error args)
v-or-error))
(def success? (complement error?))
(defn- lookup-var [env {::keys [name]}]
(get env name))
(defn- parse-var [s {::keys [parser] :as var}]
(try
(let [parsed (parser s)]
(if (nil? parsed)
(error (read-result var s))
(ok (read-result var s parsed) parsed)))
(catch Exception e
(error (read-result var s)))))
(defn- read-var [env var]
(if-let [val (lookup-var env var)]
(parse-var val var)
(if-let [default (::default var)]
(let [v (::default-value default)]
(ok (read-result var nil v) v))
(error (read-result var nil)))))
(declare -read-env)
(defn- add-to-result [acc-result [k result]]
(if (and (success? acc-result) (success? result))
(-> acc-result
(assoc-in [::value k] (::value result))
(update ::ok concat (::ok result)))
{::error (concat (::error acc-result) (::error result))
::ok (concat (::ok acc-result) (::ok result))}))
(defn- read-env-map
[env var-map]
(->> var-map
(map (fn [[k v]]
[k (-read-env env v)]))
(reduce add-to-result {})))
(defn -read-env [env x]
(cond (env-var? x)
(read-var env x)
(map? x)
(read-env-map env x)
:else
{::value x}))
(defn read-env
([var-map]
(read-env var-map (System/getenv)))
([var-map env]
(let [res (-read-env env var-map)]
(if (error? res)
res
(::value res)))))