specter

Samuel Ludwig 2025-10-09T15:39:48.635899Z

Specter's been very nice for cleaning up custom #malli transformers, a lot of recursive assoc-ing and dissoc-ing that's easily replaced by setval/`transform`, with some filterering for fancier property-based work

(defn kv-filterer
  "Navigates like filterer when both k-fn (called on key) and v-fn (called on val)
  return truthy."
  [k-fn v-fn]
  (path (filterer (fn [[k v]] (and (k-fn k) (v-fn v))))))

(defn- where-property
  ([prop] (path [(selected? 1 prop identity)]))
  ([prop f] (path [(selected? 1 prop f)])))

(defn strip-nil-optional-keys-transformer
  "A decoding transformer, only mounting to :map schemas, which dissocs all `:optional` keys with a nil value."
  [& {:as _opts}]
  (mt/transformer
    {:decoders
     {:map
      {:compile
       (fn [schema _]
         (let [optionals (->> schema
                           (m/children)
                           (select [ALL (where-property :optional true?) FIRST])
                           (set))]
           {:leave (fn [m]
                     (setval [map? (kv-filterer optionals nil?) ALL] NONE m))}))}}}))

🎉 4