Fork me on GitHub
#clojure-spec
<
2022-06-05
>
Bart Kleijngeld07:06:11

I have a map of which I'd like to specify two optional keys, but they must not exist both. I'm trying to express this in spec but I'm having a hard time. You can't use and and or in the :opt part of the s/keys spec either, so that makes it particularly hard. Can someone help me out?

Bart Kleijngeld12:06:25

I've come up with a solution, but would love to hear from more experienced users.

(defn only-one-key-of [kws]
  (fn [m]
    (not
     (set/subset? kws
                  (into #{} (keys m))))))

(s/def ::some-map
  (s/and
   (s/keys :req [:a]
           :opt [:b :c :d])
   (only-one-key-of [:b :c])))

(comment
  (s/valid? ::some-map {:a 1 :b 2 :c 3})  ;; correctly gives `false`
)

Alex Miller (Clojure team)13:06:02

I think that's the right approach

Bart Kleijngeld13:06:40

Nice. Thanks for your feedback

rolt13:06:29

small warning: only-one-key-of might be a bit misleading, it won't give the expected result if there are more than 2 keywords

Bart Kleijngeld13:06:50

Yes, it was not very well-defined, it also didn't for just one parameter. I've redone the implementation to be more robust now. Thanks for pointing it out though 🙂