metosin / malli

High-performance data-driven data specification library for Clojure/Script.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Schema Parsing

ikitommi opened this issue · comments

Currently, m/explain parses the values in align to the Schemas, producing :in and :path paths pointing paths in both values and schemas. This is a good base for generic parsing, e.g. Clojure Spec conform. We could reuse the m/-explain to collect paths for valid & invalid errors. The resulting ~conformed data structure could be self-describing, so that no m/-unexplain would be needed to turn the parsed data structure back into value, like spec has s/unform. This could be achieved using special types/records to mark branching, instead of just maps and vectors (which can't be found from values without per-schema method).

From spec:

(s/def ::name-or-id (s/or :name string?
                          :id   int?))

(s/conform ::name-or-id "abc")
;;=> [:name "abc"]

(s/conform ::name-or-id 100)
;;=> [:id 100]

Would be ~:

(m/parse [:or string? int?] "abd")
;; => #Branch{:path 0, :value "abc"}

(m/parse [:or string? int?] 100)
;; => #Branch{:path 1, :value 100}

And with named branches:

(m/parse [:orn [:string string?] [:int int?]] "abd")
;; => #Branch{:path :string, :value "abc"}

(m/parse [:orn [:string string?] [:int int?]] 100)
;; => #Branch{:path :int, :value 100}

As a bonus, we could present errors in the parse tree in-place.

See also #180.

I would also like to see something like s/conformer in malli:

https://clojuredocs.org/clojure.spec.alpha/conformer

ported the conformer example against the current api: validate + encode + validate + encode.

(def Schema1 [:string {:encode/conform #(mapv str (seq %))}])
(def Schema2 [:sequential {:encode/conform #(apply str %)} [:enum "0" "1"]])

(def conformer (mt/transformer {:name :conform}))

(as-> "1011" $
      (do (println $ "=>" (m/validate Schema1 $)) $)
      (m/encode Schema1 $ conformer)
      (do (println (pr-str $) "=>" (m/validate Schema2 $)) $)
      (m/encode Schema2 $ conformer)
      (= "1011" $))
; 1011 => true
; ["1" "0" "1" "1"] => true
; => true

#312 has most of the required logic, but only for regex schemas so I haven't added any public API. Destructuring other schemas should be much more straightforward though.

Working on this in #317. Although maybe this should have a separate PR but I'll see how it goes.

And I put zero thought into the design, just trying to get something workable to demonstrate that #317 can also support this. So it is just the obvious cross between malli.core/explainer and clojure.spec.alpha/conform.

explain & parse can be merged later. This is in master. Thanks!!