Fork me on GitHub
#clojure-spec
<
2022-08-04
>
Stel Abrego20:08:35

Howdy y'all, I'm wondering if it's possible to "bubble up" a spec error from s/every. Here's what I mean:

(s/def :nuzzle/title string?)
(s/def :nuzzle/rss? boolean?)
(s/def :nuzzle/page-entry (s/keys :req [:nuzzle/title] :opt [:nuzzle/rss?]))
(s/def :nuzzle/user-config
  (s/and
   (s/keys :req [:nuzzle/base-url])
   (s/every (fn [[key _]] (or (keyword? key) (vector? key))))
   (s/every (fn [[key val]] (or (not (vector? key))
                                (s/valid? :nuzzle/page-entry val))))))
(s/explain
 :nuzzle/user-config
 {:nuzzle/base-url ""
  [:blog-posts :test-post] {:nuzzle/title 234 :nuzzle/rss? true}})
For every key-value pair in the config map where the key is a vector, I want to validate the associated value against a spec (in this case :nuzzle/page-entry). I'm using s/every because vector keys can be arbitrary but their values must conform to :nuzzle/page-entry. The problem is that when a schema error occurs for a :nuzzle/page-entry (for example :nuzzle/title is not a string), I get an s/explain output like this:
[[:blog-posts :test-post] #:nuzzle{:title 234, :rss? true}] - failed: (fn [[key val]] (or (not (vector? key)) (valid? :nuzzle/page-entry val))) in: [1] spec: :nuzzle/user-config
Is there any way to refactor this to get a better error message about the specific problematic key-value pair (`:nuzzle/title 234`)?

Alex Miller (Clojure team)20:08:26

I don't know if this will help but you may find the techniques used in https://cognitect.com/blog/2017/1/3/spec-destructuring to be helpful here

Alex Miller (Clojure team)20:08:33

that is, treating the map as a coll of an s/or of k/v tuple types (rather than as a map)

Stel Abrego20:08:17

@U064X3EF3 I will read up on this, thank you!