Skip to content

Commit 21538e1

Browse files
committed
feat: use a custom Tagged record instead of MapEntry in parse output
Using a MapEntry was confusing users, because it printed like a vector, but you couldn't give a vector to unparse. The current method of using MapEntry was broken for weird schemas: ``` (def schema [:or [:tuple :string :keyword] [:orn ["any" :keyword]]]) (->> (m/parse schema :any) (m/unparse schema)) ; => ["any" :any] ; should've been :any ``` Changes the parse behaviour for (at least) :orn, :altn and :multi Some place (like the entry parsers) used miu/-tagged to generate MapEntry values. These use sites now use the new miu/-entry. This keeps the surface area of this change a lot smaller since we don't need to touch all the map entry logic. fixes #1123 replaces #1140
1 parent 5941195 commit 21538e1

9 files changed

+74
-43
lines changed

src/malli/core.cljc

+5-5
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@
385385
;;
386386

387387
(defn -simple-entry-parser [keyset children forms]
388-
(let [entries (map (fn [[k p s]] (miu/-tagged k (-val-schema s p))) children)]
388+
(let [entries (map (fn [[k p s]] (miu/-entry k (-val-schema s p))) children)]
389389
(reify EntryParser
390390
(-entry-keyset [_] keyset)
391391
(-entry-children [_] children)
@@ -606,7 +606,7 @@
606606
(reify EntryParser
607607
(-entry-keyset [_] keyset)
608608
(-entry-children [_] @children)
609-
(-entry-entries [_] (-vmap (fn [[k p s]] (miu/-tagged k (-val-schema s p))) @children))
609+
(-entry-entries [_] (-vmap (fn [[k p s]] (miu/-entry k (-val-schema s p))) @children))
610610
(-entry-forms [_] (->> @children (-vmap (fn [[k p v]] (if p [k p (-form v)] [k (-form v)]))))))))
611611

612612
(defn -from-entry-ast [parent ast options]
@@ -872,8 +872,8 @@
872872
(let [unparsers (into {} (map (fn [[k _ c]] [k (-unparser c)])) (-children this))]
873873
(fn [x]
874874
(if (miu/-tagged? x)
875-
(if-some [unparse (get unparsers (key x))]
876-
(unparse (val x))
875+
(if-some [unparse (get unparsers (:key x))]
876+
(unparse (:value x))
877877
::invalid)
878878
::invalid))))
879879
(-transformer [this transformer method options]
@@ -1650,7 +1650,7 @@
16501650
(fn [x] (if-some [parser (find (dispatch x))] (parser x) ::invalid))))
16511651
(-unparser [_]
16521652
(let [unparsers (reduce-kv (fn [acc k s] (assoc acc k (-unparser s))) {} @dispatch-map)]
1653-
(fn [x] (if (miu/-tagged? x) (if-some [f (unparsers (key x))] (f (val x)) ::invalid) ::invalid))))
1653+
(fn [x] (if (miu/-tagged? x) (if-some [f (unparsers (:key x))] (f (:value x)) ::invalid) ::invalid))))
16541654
(-transformer [this transformer method options]
16551655
;; FIXME: Probably should not use `dispatch`
16561656
;; Can't use `dispatch` as `x` might not be valid before it has been unparsed:

src/malli/destructure.cljc

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@
9696
(cond->> :always (conj [:*]) (not rest) (conj [:schema])))]]
9797
schema)))
9898

99-
(defn -transform [{[k v] :arg schema :schema :as all} options rest]
99+
(defn -transform [{{k :key v :value} :arg schema :schema :as all} options rest]
100100
(cond (and schema rest) (let [s (-transform all options false)] (if (-any? s) schema s))
101101
schema schema
102102
(= :vec k) (-vector v options)

src/malli/experimental.cljc

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
_ (when (= ::m/invalid parsed) (m/-fail! ::parse-error {:schema schema, :args args}))
4242
parse (fn [{:keys [args] :as parsed}] (merge (md/parse args) parsed))
4343
->schema (fn [{:keys [schema]}] [:=> schema (:schema return :any)])
44-
single (= :single (key arities))
45-
parglists (if single (->> arities val parse vector) (->> arities val :arities (map parse)))
44+
single (= :single (:key arities))
45+
parglists (if single (->> arities :value parse vector) (->> arities :value :arities (map parse)))
4646
raw-arglists (map :raw-arglist parglists)
4747
schema (as-> (map ->schema parglists) $ (if single (first $) (into [:function] $)))
4848
bodies (map (fn [{:keys [arglist prepost body]}] `(~arglist ~prepost ~@body)) parglists)
@@ -60,7 +60,7 @@
6060
~@(some-> doc vector)
6161
~enriched-meta
6262
~@bodies
63-
~@(when-not single (some->> arities val :meta vector))))]
63+
~@(when-not single (some->> arities :value :meta vector))))]
6464
(m/=> ~name ~schema)
6565
defn#)))
6666

src/malli/impl/regex.cljc

+2-2
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,8 @@
256256
(let [unparsers (into {} unparsers)]
257257
(fn [x]
258258
(if (miu/-tagged? x)
259-
(if-some [kv (find unparsers (key x))]
260-
((val kv) (val x))
259+
(if-some [kv (find unparsers (:key x))]
260+
((val kv) (:value x))
261261
:malli.core/invalid)
262262
:malli.core/invalid))))
263263

src/malli/impl/util.cljc

+6-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@
55

66
(def ^:const +max-size+ #?(:clj Long/MAX_VALUE, :cljs (.-MAX_VALUE js/Number)))
77

8-
(defn -tagged [k v] #?(:clj (MapEntry. k v), :cljs (MapEntry. k v nil)))
9-
(defn -tagged? [v] (instance? MapEntry v))
8+
(defn -entry [k v] #?(:clj (MapEntry. k v), :cljs (MapEntry. k v nil)))
9+
10+
(defrecord Tagged [key value])
11+
12+
(defn -tagged [key value] (->Tagged key value))
13+
(defn -tagged? [x] (instance? Tagged x))
1014

1115
(defn -invalid? [x] #?(:clj (identical? x :malli.core/invalid), :cljs (keyword-identical? x :malli.core/invalid)))
1216
(defn -map-valid [f v] (if (-invalid? v) v (f v)))

test/malli/core_test.cljc

+12-8
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@
257257
(is (= (miu/-tagged :pos 1) (m/parse schema* 1)))
258258
(is (= ::m/invalid (m/parse schema* 0)))
259259
(is (= 1 (m/unparse schema* (miu/-tagged :pos 1))))
260+
(is (= ::m/invalid (m/unparse schema* [:pos 1])))
260261
(is (= ::m/invalid (m/unparse schema* (miu/-tagged :pos 0))))
261262

262263
(doseq [schema [schema schema*]]
@@ -1169,8 +1170,11 @@
11691170
(is (= ::m/invalid (m/parse schema invalid5)))
11701171
(is (= ::m/invalid (m/parse schema invalid6)))
11711172
(is (= valid1 (m/unparse schema (m/parse schema valid1))))
1173+
(is (= valid1 (m/unparse schema (miu/-tagged :sized valid1))))
11721174
(is (= valid2 (m/unparse schema (m/parse schema valid2))))
1175+
(is (= valid2 (m/unparse schema (miu/-tagged :human valid2))))
11731176
(is (= valid3 (m/unparse schema (m/parse schema valid3))))
1177+
(is (= valid3 (m/unparse schema (miu/-tagged :sized valid3))))
11741178
(is (= ::m/invalid (m/unparse schema invalid1)))
11751179
(is (= ::m/invalid (m/unparse schema invalid2)))
11761180
(is (= ::m/invalid (m/unparse schema invalid3)))
@@ -3206,10 +3210,10 @@
32063210
["name" 'str]
32073211
[::m/default [:map-of 'str 'str]]]
32083212
valid {:id 1, "name" "tommi", "kikka" "kukka", "abba" "jabba"}]
3209-
(is (= {:id [::int 1],
3210-
"name" [::str "tommi"]
3211-
[::str "kikka"] [::str "kukka"]
3212-
[::str "abba"] [::str "jabba"]}
3213+
(is (= {:id (miu/-tagged ::int 1)
3214+
"name" (miu/-tagged ::str "tommi")
3215+
(miu/-tagged ::str "kikka") (miu/-tagged ::str "kukka")
3216+
(miu/-tagged ::str "abba") (miu/-tagged ::str "jabba")}
32133217
(m/parse schema valid)))
32143218
(is (= valid (->> valid (m/parse schema) (m/unparse schema))))
32153219
(is (= ::m/invalid (m/parse schema {"kukka" 42})))))
@@ -3310,7 +3314,7 @@
33103314
value [:a]]
33113315
(is (= true (m/validate schema value)))
33123316
(is (= nil (m/explain schema value)))
3313-
(is (= [[:a :a]] (m/parse schema value)))
3317+
(is (= [(miu/-tagged :a :a)] (m/parse schema value)))
33143318
(is (= value (m/unparse schema (m/parse schema value))))
33153319
(is (= value (m/decode schema value nil))))))
33163320

@@ -3422,14 +3426,14 @@
34223426
parsed (m/parse [:seqable [:orn [:l :int] [:r :boolean]]] original)
34233427
unparsed (m/unparse [:seqable [:orn [:l :int] [:r :boolean]]] parsed)]
34243428
(is (= original unparsed))
3425-
(is (= [[:l 0] [:r true] [:l 1] [:r false] [:l 2] [:r true] [:l 3] [:r false] [:l 4] [:r true] [:l 5]
3426-
[:r false] [:l 6] [:r true] [:l 7] [:r false] [:l 8] [:r true] [:l 9] [:r false]]
3429+
(is (= [(miu/-tagged :l 0) (miu/-tagged :r true) (miu/-tagged :l 1) (miu/-tagged :r false) (miu/-tagged :l 2) (miu/-tagged :r true) (miu/-tagged :l 3) (miu/-tagged :r false) (miu/-tagged :l 4) (miu/-tagged :r true) (miu/-tagged :l 5)
3430+
(miu/-tagged :r false) (miu/-tagged :l 6) (miu/-tagged :r true) (miu/-tagged :l 7) (miu/-tagged :r false) (miu/-tagged :l 8) (miu/-tagged :r true) (miu/-tagged :l 9) (miu/-tagged :r false)]
34273431
parsed)))
34283432
(let [original (sorted-set 1 2 3)
34293433
parsed (m/parse [:seqable [:orn [:a :int]]] original)
34303434
unparsed (m/unparse [:seqable [:orn [:a :int]]] parsed)]
34313435
(is (= unparsed [1 2 3]))
3432-
(is (= parsed [[:a 1] [:a 2] [:a 3]]))))
3436+
(is (= parsed [(miu/-tagged :a 1) (miu/-tagged :a 2) (miu/-tagged :a 3)]))))
34333437

34343438
(deftest every-schema-test
34353439
(is (m/validate [:every :int] nil))

test/malli/destructure_test.cljc

+41-18
Original file line numberDiff line numberDiff line change
@@ -46,25 +46,48 @@
4646
:schema [:cat
4747
:any
4848
[:orn
49-
[:map [:map
50-
[:b {:optional true} :any]
51-
["c" {:optional true} :any]
52-
['d {:optional true} :any]
53-
['demo/e {:optional true} :any]
54-
[:demo/f {:optional true}]
55-
[:demo/g {:optional true}]
56-
[123 {:optional true} :any]]]
49+
;; Unfortunately, the output order is different between clj and cljs, and we use strict equality in the test
50+
[:map #?(:clj
51+
[:map
52+
[:b {:optional true} :any]
53+
["c" {:optional true} :any]
54+
['d {:optional true} :any]
55+
['demo/e {:optional true} :any]
56+
[:demo/f {:optional true}]
57+
[123 {:optional true} :any]
58+
[:demo/g {:optional true}]]
59+
:cljs
60+
[:map
61+
[:b {:optional true} :any]
62+
["c" {:optional true} :any]
63+
['d {:optional true} :any]
64+
['demo/e {:optional true} :any]
65+
[:demo/f {:optional true}]
66+
[:demo/g {:optional true}]
67+
[123 {:optional true} :any]])]
5768
[:args [:schema
58-
[:*
59-
[:alt
60-
[:cat [:= :b] :any]
61-
[:cat [:= "c"] :any]
62-
[:cat [:= 'd] :any]
63-
[:cat [:= 'demo/e] :any]
64-
[:cat [:= :demo/f] :demo/f]
65-
[:cat [:= :demo/g] :demo/g]
66-
[:cat [:= 123] :any]
67-
[:cat [:not [:enum :b "c" 'd 'demo/e :demo/f :demo/g 123]] :any]]]]]]]
69+
#?(:clj
70+
[:*
71+
[:alt
72+
[:cat [:= :b] :any]
73+
[:cat [:= "c"] :any]
74+
[:cat [:= 'd] :any]
75+
[:cat [:= 'demo/e] :any]
76+
[:cat [:= :demo/f] :demo/f]
77+
[:cat [:= 123] :any]
78+
[:cat [:= :demo/g] :demo/g]
79+
[:cat [:not [:enum :b "c" 'd 'demo/e :demo/f 123 :demo/g]] :any]]]
80+
:cljs
81+
[:*
82+
[:alt
83+
[:cat [:= :b] :any]
84+
[:cat [:= "c"] :any]
85+
[:cat [:= 'd] :any]
86+
[:cat [:= 'demo/e] :any]
87+
[:cat [:= :demo/f] :demo/f]
88+
[:cat [:= :demo/g] :demo/g]
89+
[:cat [:= 123] :any]
90+
[:cat [:not [:enum :b "c" 'd 'demo/e :demo/f :demo/g 123]] :any]]])]]]]
6891
:errors '[[{::keysz [z]}]
6992
[{:kikka/keyz [z]}]]}
7093
{:name "map destructuring with required-keys"

test/malli/distributive_test.cljc

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@
139139
[4 [:map [:y [:= 2]] [:z [:= 4]]]]]]])))
140140

141141
(deftest parse-distributive-multi-test
142-
(is (= [1 [3 {:y 1, :z 3}]]
142+
(is (= (miu/-tagged 1 (miu/-tagged 3 {:y 1, :z 3}))
143143
(m/parse
144144
[:merge
145145
[:multi {:dispatch :y}

test/malli/util_test.cljc

+3-3
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,7 @@
878878
[:z {:optional true} :boolean]] (m/form (m/deref s))))
879879
(is (= true (m/validate s {:x "x", :y 1, :z true})))
880880
(is (= false (m/validate s {:x "x", :y "y"})))
881-
(is (= {:x [:str "x"], :y 1, :z true} (m/parse s {:x "x", :y 1, :z true})))))
881+
(is (= {:x (miu/-tagged :str "x"), :y 1, :z true} (m/parse s {:x "x", :y 1, :z true})))))
882882

883883
(testing "union"
884884
(let [s (->> [:union
@@ -891,7 +891,7 @@
891891
(is (= [:map [:x [:or [:orn [:str :string]] :int]]] (m/form (m/deref s))))
892892
(is (= true (m/validate s {:x "x"}) (m/validate s {:x 1})))
893893
(is (= false (m/validate s {:x true})))
894-
(is (= {:x [:str "x"]} (m/parse s {:x "x"})))
894+
(is (= {:x (miu/-tagged :str "x")} (m/parse s {:x "x"})))
895895
(is (= {:x 1} (m/parse s {:x 1})))))
896896

897897
(testing "merge vs union"
@@ -942,7 +942,7 @@
942942
(m/form (m/deref s))))
943943
(is (= true (m/validate s {:x "x", :z "z"})))
944944
(is (= false (m/validate s {:x "x", :y "y" :z "z"})))
945-
(is (= {:x [:str "x"], :z "z"} (m/parse s {:x "x", :z "z"})))))))
945+
(is (= {:x (miu/-tagged :str "x"), :z "z"} (m/parse s {:x "x", :z "z"})))))))
946946

947947
(def Int (m/schema int?))
948948

0 commit comments

Comments
 (0)