From d7ea4675c212b0f4fe41dcde882a67e1964421f8 Mon Sep 17 00:00:00 2001 From: Ambrose Bonnaire-Sergeant Date: Fri, 30 Aug 2024 18:34:53 -0500 Subject: [PATCH] Close #453: best-effort ordered s/enum print via cached field --- CHANGELOG.md | 3 +++ src/cljc/schema/core.cljc | 10 ++++++++-- test/cljc/schema/core_test.cljc | 21 ++++++++++++++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3a4f6cc..4bbd1d55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/cljc/schema/core.cljc b/src/cljc/schema/core.cljc index 75d8fd1d..72fdc56a 100644 --- a/src/cljc/schema/core.cljc +++ b/src/cljc/schema/core.cljc @@ -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) diff --git a/test/cljc/schema/core_test.cljc b/test/cljc/schema/core_test.cljc index 7b12ed2c..aea9e6d2 100644 --- a/test/cljc/schema/core_test.cljc +++ b/test/cljc/schema/core_test.cljc @@ -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?)]