What is the most idiomatic way to spec "if this key exists in a map, that key should not exist?" I'm finding this a bit more confusing than I initially expected. A short example:
;; map with either short or long name but not both
(def a-map {:short-name "foo"}) ; should be valid
(def a-bad-map {:short-name "foo" :long-name "foobar"}) ; should be invalid
I've tried various combinations of s/and , not and other core fn's without luck.my latest try, for example, blows up the stack:
(s/def ::short-route-name (s/and string? #(s/invalid? (s/conform ::long-route-name %))))
(s/def ::long-route-name (s/and string? #(s/invalid? (s/conform ::short-route-name %))))
I also tried using or but that fails too.spec favors open maps by design; it doesn't really support statements like "this key shouldn't exist"
You can use a predicate like #(not (and (:foo %) (:bar %)))
I saw that Malli has both an enum spec and an option to close maps but I want to learn spec before I give Malli a try, even though both those features would be useful to me here
There are also third party libraries, like https://github.com/bhauman/spell-spec that "close" specs if you really want that functionality.
I wouldn't try to model "negative" specs from within spec, it's not designed for that.
Well I guess I feel slightly less silly then for finding this pretty non-obvious
An interesting approach is that followed by cognitect's aws-api; instead of saying "this is what a valid response doesn't look like", it specs anomalies: https://github.com/cognitect-labs/anomalies
I'll have to check that out, thanks @pavlos