From c0e631a363fb51e0a7ebd95406b838cf3b1b7551 Mon Sep 17 00:00:00 2001 From: lread Date: Thu, 25 Jun 2026 15:38:20 -0400 Subject: [PATCH 1/2] tweak negation error message See https://github.com/babashka/cli/pull/184#discussion_r3477091623 Also: lifted `(option-label km k)` to `let` binding. --- src/babashka/cli.cljc | 12 +++++++----- test/babashka/cli_test.cljc | 8 ++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/babashka/cli.cljc b/src/babashka/cli.cljc index 0c98ccd..aa784a7 100644 --- a/src/babashka/cli.cljc +++ b/src/babashka/cli.cljc @@ -367,17 +367,19 @@ (let [data (ex-data e) km (::opt->flag m-meta) flag (get km k) - iv (:implicit-value data)] + iv (:implicit-value data) + label (option-label km k)] (error-fn (cond-> {:cause :coerce ;; same shape as validate: name the option, then the reason. ;; when implicit-value, give a more nuanced error message ;; instead of "cannot transform (implicit) true to ..." :msg (case iv - true (str "Missing value for option " (option-label km k)) + true (str "Missing value for option " label) ;; NOTE: squint lacks clojure.string/replace-first until > 0.14.196; use JS interop for now - false (str "Cannot negate option " #?(:squint (.replace (option-label km k) "no-" "") - :default (str/replace-first (option-label km k) "no-" ""))) - (str "Invalid value for option " (option-label km k) ": " + false (str "Negation " label " invalid for option " + #?(:squint (.replace label "no-" "") + :default (str/replace-first label "no-" ""))) + (str "Invalid value for option " label ": " (coerce-failure-reason (:input data) iv (:coerce-fn data)))) :option k :value v diff --git a/test/babashka/cli_test.cljc b/test/babashka/cli_test.cljc index 265bd11..702a3ea 100644 --- a/test/babashka/cli_test.cljc +++ b/test/babashka/cli_test.cljc @@ -114,19 +114,19 @@ (testing "--no-foo throws on non-boolean" (doseq [coerce-fn [:long :number :symbol :keyword :string]] (is (thrown-with-msg? - #?(:cljd Object :default Exception) #"Cannot negate option --foo" + #?(:cljd Object :default Exception) #"Negation --no-foo invalid for option --foo" (cli/parse-opts ["--no-foo"] {:coerce {:foo coerce-fn}})) (str "for coerce to: " coerce-fn)) (is (thrown-with-msg? - #?(:cljd Object :default Exception) #"Cannot negate option --foo" + #?(:cljd Object :default Exception) #"Negation --no-foo invalid for option --foo" (cli/parse-opts ["--no-foo"] {:coerce {:foo [coerce-fn]}})) (str "for coerce to: [" coerce-fn "]")) (is (thrown-with-msg? - #?(:cljd Object :default Exception) #"Cannot negate option :foo" + #?(:cljd Object :default Exception) #"Negation :no-foo invalid for option :foo" (cli/parse-opts [":no-foo"] {:coerce {:foo coerce-fn}})) (str "for coerce to: " coerce-fn)) (is (thrown-with-msg? - #?(:cljd Object :default Exception) #"Cannot negate option :foo" + #?(:cljd Object :default Exception) #"Negation :no-foo invalid for option :foo" (cli/parse-opts [":no-foo"] {:coerce {:foo [coerce-fn]}})) (str "for coerce to: [" coerce-fn "]"))))) From 7cced8592722f761a2129ae2a31cfe344c7ef5ae Mon Sep 17 00:00:00 2001 From: lread Date: Thu, 25 Jun 2026 15:45:09 -0400 Subject: [PATCH 2/2] regen API.md --- API.md | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/API.md b/API.md index 0020679..cade23a 100644 --- a/API.md +++ b/API.md @@ -61,7 +61,7 @@ Terminates the process after `dispatch`'s `:help` option prints an *error* Must exit or throw. Default: `System/exit` (JVM), `js/process.exit` (Node), `throw` (browser). -

Source

+

Source

## `apply-defaults` ``` clojure @@ -76,7 +76,7 @@ Fills missing keys in `m` from defaults. Existing keys in `m` win. Supported options: * `:exec-args` - map of defaults. Not subject to `:restrict`. * `:spec` - spec; `:default` entries become defaults via `spec->opts`. -

Source

+

Source

## `auto-coerce` ``` clojure @@ -89,7 +89,7 @@ Auto-coerces `s` to data. Does not coerce when `s` is not a string. * is `true` or `false`, it is coerced as boolean * starts with number, it is coerced as a number (through Clojure's `edn/read-string`) * starts with `:`, it is coerced as a keyword (through [`parse-keyword`](#babashka.cli/parse-keyword)) -

Source

+

Source

## `coerce` ``` clojure @@ -101,7 +101,7 @@ Coerce string `s` using `f`. Does not coerce when `s` is not a string. `f` may be a keyword (`:boolean`, `:int`, `:double`, `:symbol`, `:keyword`) or a function. When `f` return `nil`, this is interpreted as a parse failure and throws. -

Source

+

Source

## `coerce-opts` ``` clojure @@ -123,7 +123,7 @@ Coerces values in the map `m` using the provided configuration. `"--foo"`, `"-f"`, or `":foo"`), as opposed to `:option`, the normalized keyword (`:foo`). It lets a handler echo what the user actually typed rather than reconstruct it. It is omitted when no originating token is known. -

Source

+

Source

## `default-width-fn` ``` clojure @@ -136,7 +136,7 @@ The default `:max-width-fn` for [`format-table`](#babashka.cli/format-table)/[`f width or nil: node `process.stdout.columns`, else `$COLUMNS`, else a JLine provider probe (clj, when JLine is on the classpath, e.g. babashka), else nil (the caller then falls back to 80). -

Source

+

Source

## `dispatch` ``` clojure @@ -209,7 +209,7 @@ Command dispatcher. Each entry in the table may have additional [`parse-args`](#babashka.cli/parse-args) options. For more information and examples, see [README.md](README.md#commands). -

Source

+

Source

## `format-command-error` ``` clojure @@ -233,7 +233,7 @@ Render a terse, helpful message (a string) for a dispatch error. this, then calls [`*exit-fn*`](#babashka.cli/*exit-fn*)). Call it from a custom `:error-fn` to keep the standard message and add your own output. `--help`/`-h` is not an error - it goes to the `:help-fn`, rendered by [`format-command-help`](#babashka.cli/format-command-help). -

Source

+

Source

## `format-command-help` ``` clojure @@ -278,7 +278,7 @@ Render conventional `--help` text (a string) for the command at path `cmds` This is the renderer the `:help` option uses; call it from a custom `:help-fn` to render the standard help and then add your own output. An entry may carry `:no-doc true` to be omitted from `Commands:`. -

Source

+

Source

## `format-opts` ``` clojure @@ -289,7 +289,7 @@ Function. Formats options into an options usage help string. See [Printing options](/README.md#printing-options). -

Source

+

Source

## `format-table` ``` clojure @@ -302,7 +302,7 @@ Function. Formats `rows` into a table (string). See [Printing options](/README.md#printing-options). -

Source

+

Source

## `merge-opts` ``` clojure @@ -311,7 +311,7 @@ Formats `rows` into a table (string). Function. Merges babashka CLI options. -

Source

+

Source

## `opts->table` ``` clojure @@ -321,7 +321,7 @@ Function. Converts options to a table of rows. See [Printing options](/README.md#printing-options). -

Source

+

Source

## `parse-args` ``` clojure @@ -335,7 +335,7 @@ Same as [`parse-opts`](#babashka.cli/parse-opts) with return data reshaped. Returns a map with: * `:opts` parsed opts * `:args` remaining unparsed `args` -

Source

+

Source

## `parse-cmds` ``` clojure @@ -347,7 +347,7 @@ Function. Parses sub-commands (arguments not starting with an option prefix). Returns a map with: * `:cmds` - The parsed commands * `:args` - The remaining (unparsed) arguments -

Source

+

Source

## `parse-keyword` ``` clojure @@ -356,7 +356,7 @@ Parses sub-commands (arguments not starting with an option prefix). Returns a ma Function. Parse keyword from `s`. Ignores leading `:`. -

Source

+

Source

## `parse-opts` ``` clojure @@ -395,7 +395,7 @@ Returns a map of options parsed from command line arguments `args`, a seq of str ;; => throws 'Unknown option --qux' exception b/c there is no :qux key in the spec ``` See also: [`parse-args`](#babashka.cli/parse-args) -

Source

+

Source

## `parse-opts*` ``` clojure @@ -406,7 +406,7 @@ Function. Parses CLI `args` into a raw opts map. Returns string values unchanged (no coercion), does not apply `:exec-args` defaults, does not run `:restrict`/`:require`/`:validate`. Result map includes - `:org.babashka/cli` metadata and internal `::implicit-true-keys` / + `:org.babashka/cli` metadata and internal `::implicit-values` / `::keys-order` metadata used by `coerce-opts`. Use this when you want to merge other sources (e.g. config files) @@ -415,7 +415,7 @@ Parses CLI `args` into a raw opts map. Returns string values unchanged Supported options (subset of `parse-opts`): `:alias`/`:aliases`, `:coerce`, `:collect`, `:no-keyword-opts`, `:repeated-opts`, `:args->opts`, `:spec`. -

Source

+

Source

## `spec->opts` ``` clojure @@ -425,7 +425,7 @@ Parses CLI `args` into a raw opts map. Returns string values unchanged Function. Converts spec into opts format. Pass existing opts as optional second argument. -

Source

+

Source

## `table->tree` ``` clojure @@ -445,7 +445,7 @@ Converts a `dispatch` table into a tree. Each `:cmds` becomes a path of ``` A tree passed in is normalized and returned, so the function is idempotent. -

Source

+

Source

## `validate-opts` ``` clojure @@ -469,7 +469,7 @@ Validates the map `m` using the provided configuration. Returns `m`. keyword (`:foo`). It lets a handler echo what the user actually typed rather than reconstruct it. It is present for `:restrict` and `:validate`, and absent for `:require` (a missing required option was never typed, so it has no token). -

Source

+

Source

----- # babashka.cli.exec