Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions src/cljc/matcher_combinators/core.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,19 @@
Matcher
(-matcher-for [this] this)
(-matcher-for [this _] this)
(-match [_this _actual]
;; `Absent` should never be matched against directly. That happening means
;; it wasn't used in the context of a map
{::result/type :mismatch
::result/value (model/->InvalidMatcherContext
"`absent` matcher should only be used as the value in a map")
::result/weight 1})
(-match [_this actual]
;; Map matching special-cases bare `absent` via `match-kv`. When `absent`
;; is nested (e.g. inside `any-of`), map matching passes `::missing` for a
;; missing key — treat that as a successful absence match (#211).
(if (= actual ::missing)
{::result/type :match
::result/value actual
::result/weight 0}
;; Any other direct use means `absent` wasn't applied as a map value.
{::result/type :mismatch
::result/value (model/->InvalidMatcherContext
"`absent` matcher should only be used as the value in a map")
::result/weight 1}))
(-base-name [_] 'absent))

(defmethod clojure.pprint/simple-dispatch Absent [absent]
Expand Down
16 changes: 16 additions & 0 deletions test/clj/matcher_combinators/matchers_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,22 @@
:actual 3}}}
(c/match {:a (m/any-of 1 2)} {:a 3}))))

(testing "`any-of` + `absent` matches when the key is missing (#211)"
(is (match? {::result/type :match
::result/value {:c 'd}}
(c/match {:a (m/any-of 'b m/absent)} {:c 'd})))
(is (c/indicates-match?
(c/match {:a (m/any-of 'b m/absent)} {:c 'd}))))

(testing "`any-of` + `absent` still matches when the alternate value is present"
(is (match? {::result/type :match
::result/value {:a 'b}}
(c/match {:a (m/any-of 'b m/absent)} {:a 'b}))))

(testing "`any-of` + `absent` mismatches when the key has a different value"
(is (match? {::result/type :mismatch}
(c/match {:a (m/any-of 'b m/absent)} {:a 'c}))))

(testing "`any-of` + `seq-of` works great"
(is (match? {::result/type :mismatch
::result/value [1 "2" mismatch? 4 "5" mismatch?]}
Expand Down