Skip to content

Instantly share code, notes, and snippets.

@igrishaev
Created April 23, 2026 13:00
Show Gist options
  • Select an option

  • Save igrishaev/7467d35ed7d04476dab688f0708d5eee to your computer and use it in GitHub Desktop.

Select an option

Save igrishaev/7467d35ed7d04476dab688f0708d5eee to your computer and use it in GitHub Desktop.
(deftype MM [-form]
Object
(toString [_this]
(str -form)))
(defmacro mm [form]
`(new MM '~form))
(deftype Mismatch [^String -message]
Object
(toString [this]
-message))
(defn mismatch [template & args]
(new Mismatch (apply format template args)))
(defn mismatch? [x]
(instance? Mismatch x))
(defmulti match
(fn [a b]
[(type a) (type b)]))
(defn mism [a b]
`(~'mismatch (~'actual ~a) (~'expected ~b)))
(defmethod match [Object Object]
[a b]
(when-not (= a b)
(mism a b))
#_
(or (= a b)
(mismatch "%s <> %s" a b)))
(defmethod match [clojure.lang.Counted Number]
[a b]
(= (cc/count a) b))
(defmethod match [String Number]
[a b]
(= (cc/count a) b))
(defmethod match [Object Class]
[x cls]
(instance? cls x))
(defmethod match [String java.util.regex.Pattern]
[s re]
(boolean (cc/re-find re s)))
(defmethod match [Object clojure.lang.IFn]
[x ifn]
(boolean (ifn x)))
(defmethod match [clojure.lang.PersistentVector clojure.lang.PersistentVector]
[l1 l2]
(let [iter1 (clojure.lang.RT/iter l1)
iter2 (clojure.lang.RT/iter l2)]
(loop [i 0
acc []]
(let [next1? (.hasNext iter1)
next2? (.hasNext iter2)]
(case [next1? next2?]
[false false]
nil
[true true]
(let [v1 (.next iter1)
v2 (.next iter2)
result (match v1 v2)]
(if result
(conj acc (mism v1 v2))
(recur (inc i) (conj acc v1))))
t
[true false]
(mism (.next iter1) 'missing)
[false true]
(mism 'missing (.next iter2)))))
#_
(loop [i 0]
(let [next1? (.hasNext iter1)
next2? (.hasNext iter2)]
(case [next1? next2?]
[false false]
true
[true true]
(let [v1 (.next iter1)
v2 (.next iter2)
result (match v1 v2)]
(cond
(mismatch? result)
(mismatch "items mismatch, index: %s, %s and %s, reason: %s"
i v1 v2 result)
(not result)
(mismatch "items mismatch, index: %s, %s and %s"
i v1 v2)
:else
(recur (inc i))))
[true false]
(mismatch "the right collection has %s items, but the left one has more"
i)
[false true]
(mismatch "the left collection has %s items, but the right one has more"
i))))))
(defn space [n]
(clojure.string/join (repeat n \space)))
merge-with
(defmethod match [clojure.lang.PersistentArrayMap clojure.lang.PersistentArrayMap]
[m1 m2]
(let [keys2 (vec (keys m2))
len (cc/count keys2)]
(loop [i 0
path []]
(if (= i len)
true
(let [k (nth keys2 i)]
(if (contains? m1 k)
(let [v1 (get m1 k)
v2 (get m2 k)
result
(match v1 v2)
path
(conj path k)
lvl
(cc/count path)]
(cond
(mismatch? result)
(mismatch "items mismatch, path: %s%s%n-%s%n-%s%s, %nreason: %s"
path
(space (* i 2 lvl)) v1
(space (* i 2 lvl)) v2
result)
(not result)
(mismatch "items mismatch, path: %s, %s and %s"
path v1 v2)
:else
(recur (inc i) path)))
(mismatch "the left map doesn't have key %s" k)))))))
(comment
(defmethod assert-expr 'match? [msg form]
`(let [[_# v1# v2#] ~form]
(if (match v1# v2#)
(do-report {:type :pass :message ~msg
:expected '~form :actual :foo})
(do-report {:type :fail :message ~msg
:expected '~form :actual :foo})))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment