From eb627d15db6fd3914494a4e45d4d38de4adea730 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 10 Jul 2026 17:55:52 +0200 Subject: [PATCH] Set :validate of any primitive: completion + list values in error --- src/babashka/cli.cljc | 5 ++++- test/babashka/cli/completion_test.cljc | 8 ++++++++ test/babashka/cli_test.cljc | 20 ++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/babashka/cli.cljc b/src/babashka/cli.cljc index a55ee1b..b6db96b 100644 --- a/src/babashka/cli.cljc +++ b/src/babashka/cli.cljc @@ -479,7 +479,10 @@ (when-not (if (set? f) (contains? f v) (f v)) (let [ex-msg-fn (or (:ex-msg vf) (fn [{:keys [flag value]}] - (str "Invalid value for option " flag ": " value))) + (str "Invalid value for option " flag ": " value + (when (set? f) + (str ". Expected one of: " + (str/join ", " (sort (map #(if (keyword? %) (kw->str %) (str %)) f)))))))) flag (get opt->flag k)] (error-fn (cond-> {:cause :validate :msg (ex-msg-fn {:option k :value v :flag (flag-for k)}) diff --git a/test/babashka/cli/completion_test.cljc b/test/babashka/cli/completion_test.cljc index d02e30e..8e99d06 100644 --- a/test/babashka/cli/completion_test.cljc +++ b/test/babashka/cli/completion_test.cljc @@ -223,6 +223,14 @@ (is (= #{"a" "b"} (set (complete-options {:spec {:mode {:coerce :keyword :validate #{:a :b}}}} ["--mode" ""]))))) + (testing "set-valued :validate auto-completes strings" + (is (= #{"production" "staging"} + (set (complete-options {:spec {:env {:validate #{"production" "staging"}}}} + ["--env" ""]))))) + (testing "set-valued :validate auto-completes numbers" + (is (= #{"1" "2" "3"} + (set (complete-options {:spec {:n {:coerce :long :validate #{1 2 3}}}} + ["--n" ""]))))) (testing ":complete-fn receives :to-complete and parsed :opts (dependent completion)" (let [spec {:from {:coerce :string :complete ["x" "y"]} :to {:coerce :string diff --git a/test/babashka/cli_test.cljc b/test/babashka/cli_test.cljc index 9bad381..5e9357d 100644 --- a/test/babashka/cli_test.cljc +++ b/test/babashka/cli_test.cljc @@ -671,6 +671,26 @@ (is (str/includes? help "Custom")) (is (not (str/includes? help "Does a thing"))))))) +(deftest set-validate-test + (testing "a set :validate of strings validates, and lists the values on error" + (is (submap? {:env "staging"} + (cli/parse-opts ["--env" "staging"] + {:spec {:env {:validate #{"production" "staging"}}}}))) + (is (thrown-with-msg? + #?(:cljd Object :default Exception) + #"Invalid value for option --env: foo\. Expected one of: production, staging" + (cli/parse-opts ["--env" "foo"] + {:spec {:env {:validate #{"production" "staging"}}}})))) + (testing "a set :validate of numbers (coerced) validates, and lists the values on error" + (is (submap? {:n 2} + (cli/parse-opts ["--n" "2"] + {:spec {:n {:coerce :long :validate #{1 2 3}}}}))) + (is (thrown-with-msg? + #?(:cljd Object :default Exception) + #"Invalid value for option --n: 5\. Expected one of: 1, 2, 3" + (cli/parse-opts ["--n" "5"] + {:spec {:n {:coerce :long :validate #{1 2 3}}}}))))) + (defn- listed-command-names "Command names from the `Commands:` section of help/error output, in order." [s]