-
Notifications
You must be signed in to change notification settings - Fork 146
Expand file tree
/
Copy pathimpl.clj
More file actions
116 lines (104 loc) · 5.34 KB
/
impl.clj
File metadata and controls
116 lines (104 loc) · 5.34 KB
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
(ns compojure-api-kondo-hooks.plumbing.fnk.impl
(:require
[clojure.set :as set]
[schema.core :as-alias s]
[compojure-api-kondo-hooks.schema.macros :as schema-macros]))
;;;;; Helpers
(defn name-sym
"Returns symbol of x's name.
Converts a keyword/string to symbol, or removes namespace (if any) of symbol"
[x]
(with-meta (symbol (name x)) (meta x)))
;;; Parsing new fnk binding style
(declare letk-input-schema-and-body-form)
(defn- process-schematized-map
"Take an optional binding map like {a 2} or {a :- Number 2} and convert the schema
information to canonical metadata, if present."
[env binding]
(case (count binding)
1 (let [[sym v] (first binding)]
{sym v})
2 (let [[[[sym _]] [[schema v]]] ((juxt filter remove) #(= (val %) :-) binding)]
{sym v})))
;; TODO: unify this with positional version.
(defn letk-arg-bind-sym-and-body-form
"Given a single element of a single letk binding form and a current body form, return
a map {:schema-entry :body-form} where schema-entry is a tuple
[bound-key schema external-schema?], and body-form wraps body with destructuring
for this binding as necessary."
[env map-sym binding key-path body-form]
(cond (symbol? binding)
{:schema-entry []
:body-form `(let [~(name-sym binding) (get ~map-sym ~(keyword binding) ~key-path)]
~body-form)}
(map? binding)
(let [schema-fixed-binding (process-schematized-map env binding)
[bound-sym opt-val-expr] (first schema-fixed-binding)
bound-key (keyword bound-sym)]
{:schema-entry []
:body-form `(let [~(name-sym bound-sym) (get ~map-sym ~bound-key ~opt-val-expr)]
~body-form)})
(vector? binding)
(let [[bound-key & more] binding
{inner-input-schema :input-schema
inner-external-input-schema :external-input-schema
inner-map-sym :map-sym
inner-body-form :body-form} (letk-input-schema-and-body-form
env
(with-meta (vec more) (meta binding))
(conj key-path bound-key)
body-form)]
{:schema-entry []
:body-form `(let [~inner-map-sym (get ~map-sym ~bound-key ~key-path)]
~inner-body-form)})
:else (throw (ex-info (format "bad binding: %s" binding) {}))))
(defn- extract-special-args
"Extract trailing & sym and :as sym, possibly with schema metadata. Returns
[more-bindings special-args-map] where special-args-map is a map from each
special symbol found to the symbol that was found."
[env special-arg-signifier-set binding-form]
{:pre [(set? special-arg-signifier-set)]}
(let [[more-bindings special-bindings] (split-with (complement special-arg-signifier-set) binding-form)]
(loop [special-args-map {}
special-arg-set special-arg-signifier-set
[arg-signifier & other-bindings :as special-bindings] special-bindings]
(if-not (seq special-bindings)
[more-bindings special-args-map]
(do
(let [[sym remaining-bindings] (schema-macros/extract-arrow-schematized-element env other-bindings)]
(recur (assoc special-args-map arg-signifier sym)
(disj special-arg-set arg-signifier)
remaining-bindings)))))))
(defn letk-input-schema-and-body-form
"Given a single letk binding form, value form, key path, and body
form, return a map {:input-schema :external-input-schema :map-sym :body-form}
where input-schema is the schema imposed by binding-form, external-input-schema
is like input-schema but includes user overrides for binding vectors,
map-sym is the symbol which it expects the bound value to be bound to,
and body-form wraps body in the bindings from binding-form from map-sym."
[env binding-form key-path body-form]
(let [[bindings {more-sym '& as-sym :as}] (extract-special-args env #{'& :as} binding-form)
as-sym (or as-sym (gensym "map"))
[input-schema-elts
external-input-schema-elts
bound-body-form] (reduce
(fn [[input-schema-elts external-input-schema-elts cur-body] binding]
(let [{:keys [schema-entry body-form]}
(letk-arg-bind-sym-and-body-form
env as-sym binding key-path cur-body)
[bound-key input-schema external-input-schema] schema-entry]
[(conj input-schema-elts [bound-key input-schema])
(conj external-input-schema-elts
[bound-key (or external-input-schema input-schema)])
body-form]))
[[] [] body-form]
(reverse
(schema-macros/process-arrow-schematized-args
env bindings)))
explicit-schema-keys []
final-body-form (if more-sym
`(let [~more-sym (dissoc ~as-sym ~@explicit-schema-keys)]
~bound-body-form)
bound-body-form)]
{:map-sym as-sym
:body-form final-body-form}))