The following clojure spec ::my
permits maps having either the key :width or the key :height, however it does not permit having both of them:
(s/def ::width int?)
(s/def ::height int?)
(defn one-of-both? [a b]
(or (and a (not b))
(and b (not a))))
(s/def ::my (s/and (s/keys :opt-un [::width ::height])
#(one-of-both? (% :width) (% :height))))
Even if it does the job:
(s/valid? ::my {})
false
(s/valid? ::my {:width 5})
true
(s/valid? ::my {:height 2})
true
(s/valid? ::my {:width 5 :height 2})
false
the code does not appear that concise to me. First the keys are defined as optional and then as required. Does anyone have a more readable solution to this?
clojure.spec is designed to encourage specs capable of growth. Thus its s/keys
does not support forbidding keys. It even matches maps having keys that are neither in :req
or opt
.
There is however a way to say the map must at least have :width
or :height
, i.e. not XOR, just OR.
(s/def ::my (s/keys :req-un [(or ::width ::height)]))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With