-
Notifications
You must be signed in to change notification settings - Fork 4
/
spec.cljc
43 lines (39 loc) · 1.52 KB
/
spec.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
(ns com.yetanalytics.flint.spec
#?(:clj (:require [clojure.spec.alpha :as s]))
#?(:cljs (:require-macros
[com.yetanalytics.flint.spec :refer [sparql-keys]])))
;; Need these helpers to deal with `(or ::kspec-1 ::kspec-2 ...)`
#?(:clj
(defn- collect-keys
[x]
(cond
(coll? x) (->> x flatten (filter keyword?))
(keyword? x) [x]
:else nil)))
#?(:clj
(defn- collect-unq-keys
[x]
(map (comp keyword name) (collect-keys x))))
#?(:clj
(defmacro sparql-keys
"A variant of `s/keys` that automatically conforms the map into
a kv-pair vector sorted by `key-comp-fn`. In addition, keys
are restricted to those in the spec."
[& {:keys [key-comp-fn req opt req-un opt-un]
:or {key-comp-fn compare}}]
(let [keys-set# (set (concat (collect-keys req)
(collect-keys opt)
(collect-unq-keys req-un)
(collect-unq-keys opt-un)))
keys-spec# (cond-> [`s/keys]
req (conj :req req)
opt (conj :opt opt)
req-un (conj :req-un req-un)
opt-un (conj :opt-un opt-un)
true seq)]
`(s/and map?
;; `restrict-keys` taken from xapi-schema.spec
#(every? ~keys-set# (keys %))
~keys-spec#
(s/conformer #(into [] %))
(s/conformer #(sort-by first ~key-comp-fn %))))))