Skip to content

Commit 6900af1

Browse files
authored
With option input validation fix (#72)
* Add a ::fn-input? flag to prevent wrong pull-pattern validation on fn input * Add ::fn-input? flag to type map-of as well
1 parent 26e01fd commit 6900af1

File tree

1 file changed

+72
-21
lines changed

1 file changed

+72
-21
lines changed

src/sg/flybot/pullable/schema.cljc

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,40 @@
174174
;;-------------------------------
175175
; Public API
176176

177+
(defn walk-tag-fn-inputs
178+
"Walk through the given `schema` and add a flag ::fn-input? to the
179+
properties of the function arguments that are subject to be pullable."
180+
[schema]
181+
(m/walk
182+
schema
183+
(m/schema-walker
184+
(fn [sch]
185+
(if (and (= :=> (m/type sch)) (seq (m/children sch)))
186+
(let [[input output] (m/children sch)
187+
tagged-inputs (m/walk
188+
input
189+
(m/schema-walker
190+
(fn [input-sch]
191+
(let [t (m/type input-sch)]
192+
(if (or (= :map t) (= :map-of t))
193+
(mu/update-properties input-sch assoc ::fn-input? true)
194+
input-sch)))))]
195+
[:=> tagged-inputs output])
196+
sch)))))
197+
198+
^:rct/test
199+
(comment
200+
(def sch (-> [:=>
201+
[:cat [:sequential [:map {:closed true} [:a :int]]]]
202+
[:map [:b :string]]]
203+
(m/schema)
204+
(walk-tag-fn-inputs)))
205+
(mu/equals [:=>
206+
[:cat [:sequential [:map {:closed true ::fn-input? true} [:a :int]]]]
207+
[:map [:b :string]]]
208+
sch) ;=> true
209+
)
210+
177211
(defn normalize-schema
178212
"IMO malli's schema is too liberal, let's normalize it for some
179213
alternative (a.k.a should not be allowed in the first place)"
@@ -207,14 +241,18 @@
207241
(pattern-schema-of nil))
208242
([data-schema]
209243
(m/walk
210-
(or data-schema [:or
211-
[:map-of :any :any]
212-
[:sequential [:map-of :any :any]]])
244+
(-> (or data-schema [:or
245+
[:map-of :any :any]
246+
[:sequential [:map-of :any :any]]])
247+
(walk-tag-fn-inputs))
213248
(m/schema-walker
214249
(fn [sch]
215250
(let [sch (normalize-schema sch)
216251
t (m/type sch)]
217252
(cond
253+
(::fn-input? (m/properties sch))
254+
sch
255+
218256
(= :map t)
219257
(-> sch (pattern-map-schema) (mark-ptn))
220258

@@ -229,9 +267,9 @@
229267
(and (seq-type? t) (seq (m/children sch)))
230268
(let [x (-> sch m/children first)]
231269
(if (ptn? x)
232-
(-> [:cat
233-
x
234-
[:? [:fn lvar?]]
270+
(-> [:cat
271+
x
272+
[:? [:fn lvar?]]
235273
[:? [:alt [:cat [:= :seq] [:vector {:min 1 :max 2} :int]]]]]
236274
(m/schema)
237275
(mark-ptn))
@@ -245,8 +283,8 @@
245283
(def ptn-schema (pattern-schema-of (m/schema [:map [:a :int]])))
246284
(m/explain ptn-schema '{:a ?}) ;=> nil
247285
(m/explain ptn-schema '{(:a) ?}) ;=> nil
248-
(m/explain ptn-schema '{(:a :default 0) ?});=> nil (m/explain ptn-schema '{(:a default :ok) ?}) ;=>> (complement nil?)
249-
286+
(m/explain ptn-schema '{(:a :default 0) ?}) ;=> nil (m/explain ptn-schema '{(:a default :ok) ?}) ;=>> (complement nil?)
287+
250288
;;nesting pattern
251289
(def ptn-schema2 (pattern-schema-of [:map [:a [:map [:b :int]]]]))
252290
(m/explain ptn-schema2 '{:a {:b :ok}}) ;=>> (complement nil?)
@@ -256,11 +294,11 @@
256294
(m/validate ptn-schema2 '{:b ?});=> false
257295
;;disallow directly fetch nesting
258296
(m/explain ptn-schema2 '{:a ?}) ;=>> (complement nil?)
259-
297+
260298
;;sequential pattern
261299
(def ptn-schema3 (pattern-schema-of [:sequential [:map [:a :string]]]))
262300
(m/explain ptn-schema3 '[{:a ?} ?x :seq [1 2]]) ;=> nil
263-
301+
264302
;;with pattern can pick up function schema
265303
(def ptn-schema4 (pattern-schema-of [:map
266304
[:a [:=> [:cat :int :keyword] :int]]
@@ -272,35 +310,48 @@
272310
(m/explain ptn-schema4 '{(:b :with [3]) ?}) ;=>> (complement nil?)
273311
(m/explain ptn-schema4 '{(:c :with []) ?}) ;=> nil
274312
(m/explain ptn-schema4 '{(:a :batch [[3, :foo] [4, :bar]]) ?}) ;=> nil
275-
313+
276314
;;with pattern can nested
277315
(def ptn-schema5 (pattern-schema-of [:map [:a [:=> [:cat :int] [:map [:b :string]]]]]))
278316
(m/explain ptn-schema5 '{(:a :with [3]) {:b ?}}) ;=> nil
279-
317+
280318
;;for with pattern, its return type will be checked
281319
(m/explain ptn-schema5 '{(:a :with [3]) {(:b :not-found 5) ?}}) ;=>> {:errors #(= 1 (count %))}
282320
(m/explain ptn-schema5 '{(:a :with [3]) {(:b :not-found "ok") ?}}) ;=> nil
283-
321+
284322
;;multiple options check
285323
(m/explain ptn-schema5 {(list :a :not-found str :with [:ok])
286324
{(list :b :not-found 4) '?}}) ;=>> {:errors #(= 2 (count %))}
287-
325+
288326
;;batch result testing
289327
(m/explain ptn-schema5 '{(:a :batch [[3] [2]]) {(:b :not-found "ok") ?}}) ;=> nil
290-
328+
291329
;;for with pattern, its input type will be checked
292330
(def ptn-schema6 (pattern-schema-of [:map [:a [:=> [:cat [:map [:b1 :int]]] :string]]]))
293-
(m/explain ptn-schema6 '{(:a :with [{}]) ?}) ;=>> {:errors #(= 1 (count %))}
294-
331+
(m/explain ptn-schema6 '{(:a :with [{}]) ?}) ;=>> {:errors #(= 1 (count %))}
332+
295333
(def ptn-schema7
296334
(pattern-schema-of
297335
[:sequential
298336
[:map
299337
[:name :string]
300338
[:op [:=> [:cat :int] :int]]]]))
301-
(m/explain ptn-schema7 '[{:name "squre" (:op :with [3]) ?}])
302-
) ;=> nil
303-
339+
(m/explain ptn-schema7 '[{:name "squre" (:op :with [3]) ?}]) ;=> nil
340+
341+
;;for with pattern, input is not pullable so seq can pass
342+
(def ptn-schema8 (pattern-schema-of
343+
[:map
344+
[:a [:=>
345+
[:cat [:sequential
346+
[:map-of
347+
[:map [:b :int]]
348+
[:map [:c :int]]]]]
349+
[:sequential [:map [:d :int]]]]]]))
350+
351+
(m/explain ptn-schema8 '{(:a :with [[{{:b 1} {:c 2}}
352+
{{:b 1} {:c 2}}]])
353+
[{:d ?}]}) ;=> nil
354+
)
304355

305356
(defn check-pattern!
306357
"check `pattern` against `data-schema`, if not conform throwing an ExceptionInfo
@@ -314,4 +365,4 @@
314365
(comment
315366
(check-pattern! nil 3) ;throws=>> some?
316367
(check-pattern! nil {}) ;=> nil
317-
)
368+
)

0 commit comments

Comments
 (0)