malli
malli copied to clipboard
[Proposal] map-of index should be part of `:in` during a walk
(defprotocol LensSchema
(-keep [this] "returns truthy if schema contributes to value path")
I think that the index in :map-of should be part of :in during a walk as it contributes to the value path, otherwise we don't differentiate between the key and the value.
Other proposal: to replace the 0 and 1 indices by something like ::m/key and ::m/val for this schema, similarly to how ::m/in is used for collections.
Thanks for reporting. I think this is even harder than that. There should be conversion from explain result of :in to :path and vice versa and that doesn't hold for :map-of.
(defn e! [schema value]
(let [{:keys [schema] :as explain} (m/explain schema value)]
(for [{:keys [path in] :as error} (-> explain :errors)]
{:schema schema
:error error
:get-in (mu/get-in schema path)
:path->in (mu/path->in schema path)
:in->paths (mu/in->paths schema in)})))
(e! [:map ["kikka" :keyword]] {"kikka" "x"})
;({:schema [:map ["kikka" :keyword]],
; :error {:path ["kikka"], :in ["kikka"], :schema :keyword, :value "x"},
; :get-in :keyword,
; :path->in ["kikka"],
; :in->paths [["kikka"]]})
(e! [:map-of :keyword :keyword] {:kikka :a, :kukka "b"})
;({:schema [:map-of :keyword :keyword],
; :error {:path [1], :in [:kukka], :schema :keyword, :value "b"},
; :get-in :keyword,
; :path->in [1], <-- this is wrong!
; :in->paths []}) <-- this too
I think there are many ways to fix this. First that comes to mind is to make m/-explain contain both schema index (e.g. :path) + the value itself, wrapped in a vector. This should be possible as the :path is fully managed via the Schema instance, so it can write anything + read it back via the LensSchema protocol.
In this the above case, we would not conj just 1 to the path when explaining, but a vector of index + value instead, e.g. [1 :kikka]. This would be unwrapped in the LensSchema.
The example would return:
(e! [:map-of :keyword :keyword] {:kikka :a, :kukka "b"})
;({:schema [:map-of :keyword :keyword],
; :error {:path [[1 :kukka]], :in [:kukka], :schema :keyword, :value "b"},
; :get-in :keyword,
; :path->in [:kukka]
; :in->paths [[1 :kukka]})
would this work for you?
PR welcome.