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)"
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
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 ))
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?)
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 ]]
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
314365(comment
315366 (check-pattern! nil 3 ) ; throws=>> some?
316367 (check-pattern! nil {}) ; => nil
317- )
368+ )
0 commit comments