/
impl.cljc
145 lines (120 loc) · 3.71 KB
/
impl.cljc
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
(ns spec-tools.impl
(:refer-clojure :exclude [resolve])
#?(:cljs (:require-macros [spec-tools.impl :refer [resolve]]))
(:require
#?(:cljs [cljs.analyzer.api])
[clojure.spec.alpha :as s]
[clojure.walk :as walk])
(:import
#?@(:clj
[(clojure.lang Var)])))
(defn in-cljs? [env]
(:ns env))
;; ClojureScript 1.9.655 and later have a resolve macro - maybe this can be
;; eventually converted to use it.
(defmacro resolve
[env sym]
`(if (in-cljs? ~env)
((clojure.core/resolve 'cljs.analyzer.api/resolve) ~env ~sym)
(clojure.core/resolve ~env ~sym)))
(defn- cljs-sym [x]
(if (map? x)
(:name x)
x))
(defn- clj-sym [x]
(if (var? x)
(let [^Var v x]
(symbol (str (.name (.ns v)))
(str (.sym v))))
x))
(defn ->sym [x]
#?(:clj (clj-sym x)
:cljs (cljs-sym x)))
(defn- unfn [cljs? expr]
(if (clojure.core/and (seq? expr)
(symbol? (first expr))
(= "fn*" (name (first expr))))
(let [[[s] & form] (rest expr)]
(conj (walk/postwalk-replace {s '%} form) '[%] (if cljs? 'cljs.core/fn 'clojure.core/fn)))
expr))
#?(:clj
(defn cljs-resolve [env symbol]
(clojure.core/or (->> symbol (resolve env) cljs-sym) symbol)))
(defn polish [x]
(cond
(seq? x) (flatten (keep polish x))
(symbol? x) nil
:else x))
(defn polish-un [x]
(some-> x polish name keyword))
(defn parse-keys [form]
(let [m (some->> form (rest) (apply hash-map))]
(cond-> m
(:req m) (update :req #(->> % flatten (keep polish) (into [])))
(:req-un m) (update :req-un #(->> % flatten (keep polish-un) (into [])))
(:opt-un m) (update :opt-un #(->> % (keep polish-un) (into []))))))
(defn extract-keys [form]
(let [{:keys [req opt req-un opt-un]} (some->> form (rest) (apply hash-map))]
(flatten (map polish (concat req opt req-un opt-un)))))
#?(:clj
(defn resolve-form [env pred]
(let [cljs? (in-cljs? env)
res (if cljs? (partial cljs-resolve env) clojure.core/resolve)]
(->> pred
(walk/postwalk
(fn [x]
(if (symbol? x)
(or (some->> x res ->sym) x)
x)))
(unfn cljs?)))))
(defn extract-pred-and-info [x]
(if (map? x)
[(:spec x) (dissoc x :spec)]
[x {}]))
(defn strip-fn-if-needed [form]
(let [head (first form)]
;; Deal with the form (clojure.core/fn [%] (foo ... %))
;; We should just use core.match...
(if (and (= (count form) 3) (= head #?(:clj 'clojure.core/fn :cljs 'cljs.core/fn)))
(nth form 2)
form)))
(defn normalize-symbol [kw]
(case (and (symbol? kw) (namespace kw))
"spec-tools.spec" (symbol "clojure.core" (name kw))
"cljs.core" (symbol "clojure.core" (name kw))
"cljs.spec.alpha" (symbol "clojure.spec.alpha" (name kw))
kw))
(defn extract-form [spec]
(if (seq? spec) spec (s/form spec)))
(defn qualified-name [key]
(if key
(if-let [nn (namespace key)]
(str nn "/" (name key))
(name key))))
(defn nilable-spec? [spec]
(let [form (and spec (s/form spec))]
(boolean
(if-not (= form ::s/unknown)
(some-> form
seq
first
#{'clojure.spec.alpha/nilable
'cljs.spec.alpha/nilable})))))
(defn unwrap
"Unwrap [x] to x. Asserts that coll has exactly one element."
[coll]
{:pre [(= 1 (count coll))]}
(first coll))
(defn deep-merge [& values]
(cond
(every? map? values)
(apply merge-with deep-merge values)
(every? coll? values)
(reduce into values)
:else
(last values)))
;;
;; FIXME: using ^:skip-wiki functions from clojure.spec. might break.
;;
(defn register-spec! [k s]
(s/def-impl k (s/form s) s))