Skip to content

Commit

Permalink
Close #453: best-effort ordered s/enum print via cached field
Browse files Browse the repository at this point in the history
  • Loading branch information
frenchy64 committed Aug 30, 2024
1 parent 8fcb57f commit d7ea467
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## NEXT
* [#449](https://github.com/plumatic/schema/issues/453): Preserve `s/enum` order during printing

## 1.4.1 (`2022-09-29`)
* [#449](https://github.com/plumatic/schema/issues/449): Fix bad jsdoc

Expand Down
10 changes: 8 additions & 2 deletions src/cljc/schema/core.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -284,15 +284,21 @@

;;; enum (in a set of allowed values)

(defn- best-effort-ordered-form [{:keys [vs] :as enum}]
(or (force (get (::form-hints enum) vs))
(cons 'enum vs)))

(macros/defrecord-schema EnumSchema [vs]
Schema
(spec [this] (leaf/leaf-spec (spec/precondition this #(contains? vs %) #(list vs %))))
(explain [this] (cons 'enum vs)))
(explain [this] (best-effort-ordered-form this)))

(clojure.core/defn enum
"A value that must be = to some element of vs."
[& vs]
(EnumSchema. (set vs)))
(let [svs (set vs)]
(-> (EnumSchema. svs)
(assoc ::form-hints {svs (delay (seq (into ['enum] (distinct) vs)))}))))


;;; pred (matches all values for which p? returns truthy)
Expand Down
21 changes: 20 additions & 1 deletion test/cljc/schema/core_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,26 @@
(valid! schema 1)
(invalid! schema :c)
(invalid! (s/enum :a) 2 "(not (#{:a} 2))")
(is (= '(1 :a :b enum) (sort-by str (s/explain schema))))))
(is (= '(enum :a :b 1) (s/explain schema))))
(is (= (cons 'enum (range 1000)) (s/explain (apply s/enum (range 1000)))))
(testing "prints as if (distinct vs), which preserves original order"
(is (= '(enum 1 2 3 4) (s/explain (s/enum 1 2 1 3 1 4)))))
(testing "still prints correctly if implementation details are exploited"
(dotimes [_ 100]
(let [[a b c] (repeatedly #(rand-nth
[(gensym)
(str (gensym))
(keyword (gensym))]))
_ (assert (distinct? a b c))
e (s/enum a b)
_ (testing "prints in order"
(is (= (list 'enum a b) (s/explain e))))
e (update e :vs conj c)
_ (testing "adding an extra entry using implementation details just prints using the set's order"
(is (= (cons 'enum (:vs e)) (s/explain e))))
e (update e :vs disj c)
_ (testing "resetting :vs preserves the original printing order"
(is (= (list 'enum a b) (s/explain (update e :vs disj c)))))]))))

(deftest pred-test
(let [schema (s/pred odd? 'odd?)]
Expand Down

0 comments on commit d7ea467

Please sign in to comment.