-
Notifications
You must be signed in to change notification settings - Fork 12
/
utils.clj
97 lines (77 loc) · 3.14 KB
/
utils.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
(ns metav.utils
(:require
[clojure.spec.alpha :as s]
[clojure.java.io :as io]
[clojure.string :as string]
[clojure.tools.logging :as log]
[me.raynes.fs :as fs]))
(defn pwd []
(str fs/*cwd*))
(defn assoc-computed [context k f]
(assoc context k (f context)))
;;----------------------------------------------------------------------------------------------------------------------
;; Spec functions
;;----------------------------------------------------------------------------------------------------------------------
(s/def ::non-empty-str (s/and string? (complement empty?)))
(defn resource? [p]
(-> p io/resource fs/exists?))
(s/def ::resource-path (s/and ::non-empty-str
resource?))
(defn ancestor? [path possible-descendant]
(string/starts-with? (str (fs/normalized possible-descendant))
(str (fs/normalized path))))
;;----------------------------------------------------------------------------------------------------------------------
;; Error handling
;;----------------------------------------------------------------------------------------------------------------------
(defmacro check
"Similar to clojure's `assert` but always on."
([x]
`(when-not ~x
(throw (ex-info (str "Check failed: " (pr-str '~x))
{:type :check-failed}))))
([x message]
`(when-not ~x
(throw (ex-info (str "Check failed: " ~message "\n" (pr-str '~x))
{:type :check-failed})))))
(defn check-spec
"Similar to `clojure.spec.alpha/assert` but always on."
[spec x]
(if (s/valid? spec x)
x
(let [ed (assoc (s/explain-data spec x)
::failure :assertion-failed)
msg (str "Spec assertion for spec `" spec "` failed\n"
(with-out-str (s/explain-out ed)))
err (ex-info
msg
ed)]
(log/error err msg)
(throw err))))
;;----------------------------------------------------------------------------------------------------------------------
;; Options handling
;;----------------------------------------------------------------------------------------------------------------------
(defmacro ensure-keys [m & kvs]
(assert (even? (count kvs)))
(let [res (gensym "res")]
`(let [~res ~m
~@(apply concat (for [[k v] (partition 2 kvs)]
`[~res (if (contains? ~res ~k)
~res
(assoc ~res ~k ~v))]))]
~res)))
(defmacro ensure-key [m k v]
(list `ensure-keys m k v))
(defn merge-defaults [m defaults]
(reduce (fn [acc k]
(ensure-key acc k (get defaults k)))
m
(keys defaults)))
(defn merge&validate [context defaults spec]
(-> context
(merge-defaults defaults)
(->> (check-spec spec))))
;;----------------------------------------------------------------------------------------------------------------------
;;----------------------------------------------------------------------------------------------------------------------
(defn side-effect-from-context! [context f!]
(f! context)
context)