In clojure.spec, how can I declare a map that accepts only certain keys?
{::a 1 ::b 2 ::BAD 3} does conform to (s/keys :req :req [::a ::b]), but I want a spec that will be bothered by ::BAD or any other undeclared key.
My use case: I am introducing spec to some legacy code, and I want to be warned if I have failed to specify some elements that may appear in my map.
Question 2:
So, assuming that this is not possible currently, I brute-forced it with:
(defn- key-checker [valid-keys]
(fn [map-to-check]
(empty? (clojure.set/difference (into #{} (keys map-to-check)) valid-keys))))
(s/def ::my-map (s/and (s/keys :req [::a ::b]) (key-checker #{::a ::b})))
Ignoring the ugly, and easily fixable, smell of the duplicated set of keys, this has a bigger problem:
If the predicate fails, the error that assert gives me is "{... big ugly map ...} fails predicate: (key-checker #{::a ::b})" with no easy way for the viewer to see which key failed. Can I somehow hook into the explain mechanism to give a more useful message?