From f298e9c80a7d8ee69a481c3389cdc7e6dcacf2c8 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Tue, 14 Nov 2023 18:23:06 +0100 Subject: [PATCH 1/2] wip --- src/babashka/cli.cljc | 3 ++- test/babashka/cli_test.cljc | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/babashka/cli.cljc b/src/babashka/cli.cljc index 40ef6cd..4646711 100644 --- a/src/babashka/cli.cljc +++ b/src/babashka/cli.cljc @@ -196,7 +196,8 @@ (assoc aliases alias k))) require (update :require (fnil #(conj % k) #{})) validate (update :validate assoc k validate) - default (update :exec-args assoc k default))) + default (update :exec-args (fn [exec-args] + (assoc exec-args k (get exec-args k default)))))) {} spec)) diff --git a/test/babashka/cli_test.cljc b/test/babashka/cli_test.cljc index 0054357..48fe979 100644 --- a/test/babashka/cli_test.cljc +++ b/test/babashka/cli_test.cljc @@ -214,7 +214,12 @@ (is (submap? #:deps{:root "the-root"} (cli/parse-opts ["--deps/root" "the-root"] - {:spec [[:deps/root {:desc "The root"}]]}))))) + {:spec [[:deps/root {:desc "The root"}]]}))) + (testing "exec-args wins over spec" + (is (= 2 (:foo (cli/parse-opts [] {:spec {:foo {:default 1}} + :exec-args {:foo 2}})))) + (is (nil? (:foo (cli/parse-opts [] {:spec {:foo {:default 1}} + :exec-args {:foo nil}}))))))) (deftest args-test (is (submap? {:foo true} (cli/parse-opts ["--foo" "--"]))) From d0b236433e9d8742b266c9d3cf42ac8e243542e1 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Tue, 14 Nov 2023 19:39:45 +0100 Subject: [PATCH 2/2] Prioritize :exec-args over spec :defaults --- API.md | 58 ++++++++++++++++++++++++++++++------- CHANGELOG.md | 4 +++ src/babashka/cli.cljc | 39 +++++++++++++------------ test/babashka/cli_test.cljc | 2 +- 4 files changed, 72 insertions(+), 31 deletions(-) diff --git a/API.md b/API.md index d424764..833719b 100644 --- a/API.md +++ b/API.md @@ -4,13 +4,18 @@ - [`coerce`](#coerce) - Coerce string s using f - [`dispatch`](#dispatch) - Subcommand dispatcher. - [`format-opts`](#format-opts) + - [`format-table`](#format-table) - [`merge-opts`](#merge-opts) - Merges babashka CLI options. - [`number-char?`](#number-char?) + - [`opts->table`](#opts->table) + - [`pad`](#pad) + - [`pad-cells`](#pad-cells) - [`parse-args`](#parse-args) - Same as parse-opts but separates parsed opts into :opts and adds - [`parse-cmds`](#parse-cmds) - Parses sub-commands (arguments not starting with an option prefix) and returns a - [`parse-keyword`](#parse-keyword) - Parse keyword from s - [`parse-opts`](#parse-opts) - Parse the command line arguments args, a seq of strings. - - [`spec->opts`](#spec-opts) - Converts spec into opts format. + - [`rows`](#rows) + - [`spec->opts`](#spec->opts) - Converts spec into opts format - [`babashka.cli.exec`](#babashkacliexec) - [`-main`](#-main) - Main entrypoint for command line usage. - [`main`](#main) @@ -79,14 +84,21 @@ Subcommand dispatcher. Each entry in the table may have additional [`parse-args`](#parse-args) options. Examples: see [README.md](README.md#subcommands). -
[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L544-L588) +
[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L567-L611) ## `format-opts` ``` clojure -(format-opts {:keys [spec indent order header], :or {indent 2}}) +(format-opts {:as cfg, :keys [indent], :or {indent 2}}) ``` -[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L504-L569) +[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L556-L560) +## `format-table` +``` clojure + +(format-table {:keys [rows indent]}) +``` + +[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L516-L527) ## `merge-opts` ``` clojure @@ -103,6 +115,27 @@ Merges babashka CLI options. ``` [source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L53-L55) +## `opts->table` +``` clojure + +(opts->table {:keys [spec order]}) +``` + +[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L537-L554) +## `pad` +``` clojure + +(pad len s) +``` + +[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L506-L506) +## `pad-cells` +``` clojure + +(pad-cells rows) +``` + +[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L508-L514) ## `parse-args` ``` clojure @@ -113,7 +146,7 @@ Merges babashka CLI options. Same as [`parse-opts`](#parse-opts) but separates parsed opts into `:opts` and adds `:cmds` and `:rest-args` on the top level instead of metadata. -
[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L459-L466) +
[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L484-L491) ## `parse-cmds` ``` clojure @@ -125,7 +158,7 @@ Same as [`parse-opts`](#parse-opts) but separates parsed opts into `:opts` and a Parses sub-commands (arguments not starting with an option prefix) and returns a map with: * `:cmds` - The parsed subcommands * `:args` - The remaining (unparsed) arguments -
[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L203-L213) +
[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L205-L215) ## `parse-keyword` ``` clojure @@ -173,16 +206,19 @@ Parse the command line arguments `args`, a seq of strings. ;; => throws 'Unknown option --qux' exception b/c there is no :qux key in the spec ``` -
[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L250-L457) +
[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L257-L482) +## `rows` +[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L530-L532) ## `spec->opts` ``` clojure (spec->opts spec) +(spec->opts spec {:keys [exec-args]}) ``` -Converts spec into opts format. -
[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L184-L201) +Converts spec into opts format. Pass existing opts as optional second argument. +
[source](https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L184-L203) # babashka.cli.exec @@ -207,11 +243,11 @@ Main entrypoint for command line usage. clojure -M:exec clojure.core prn :a 1 :b 2 ;;=> {:a "1" :b "2"} ``` -
[source](https://github.com/babashka/cli/blob/main/src/babashka/cli/exec.clj#L67-L81) +
[source](https://github.com/babashka/cli/blob/main/src/babashka/cli/exec.clj#L88-L101) ## `main` ``` clojure (main & args) ``` -[source](https://github.com/babashka/cli/blob/main/src/babashka/cli/exec.clj#L63-L65) +[source](https://github.com/babashka/cli/blob/main/src/babashka/cli/exec.clj#L83-L86) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3514a29..fb8d054 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ For breaking changes, check [here](#breaking-changes). [Babashka CLI](https://github.com/babashka/cli): turn Clojure functions into CLIs! +## Unreleased (2023-11-14) + +- Prioritize `:exec-args` over spec `:default`s + ## v0.7.53 (2023-09-28) - [#72](https://github.com/babashka/cli/issues/72): add possibility to add a header to format-opts ([@Sohalt](https://github.com/Sohalt)) diff --git a/src/babashka/cli.cljc b/src/babashka/cli.cljc index 4646711..3493364 100644 --- a/src/babashka/cli.cljc +++ b/src/babashka/cli.cljc @@ -182,24 +182,25 @@ (assoc acc current-opt arg)))) (defn spec->opts - "Converts spec into opts format." - [spec] - (reduce - (fn [acc [k {:keys [coerce alias default require validate]}]] - (cond-> acc - coerce (update :coerce assoc k coerce) - alias (update :alias - (fn [aliases] - (when (contains? aliases alias) - (throw (ex-info (str "Conflicting alias " alias " between " (get aliases alias) " and " k) - {:alias alias}))) - (assoc aliases alias k))) - require (update :require (fnil #(conj % k) #{})) - validate (update :validate assoc k validate) - default (update :exec-args (fn [exec-args] - (assoc exec-args k (get exec-args k default)))))) - {} - spec)) + "Converts spec into opts format. Pass existing opts as optional second argument." + ([spec] (spec->opts spec nil)) + ([spec {:keys [exec-args]}] + (reduce + (fn [acc [k {:keys [coerce alias default require validate]}]] + (cond-> acc + coerce (update :coerce assoc k coerce) + alias (update :alias + (fn [aliases] + (when (contains? aliases alias) + (throw (ex-info (str "Conflicting alias " alias " between " (get aliases alias) " and " k) + {:alias alias}))) + (assoc aliases alias k))) + require (update :require (fnil #(conj % k) #{})) + validate (update :validate assoc k validate) + default (update :exec-args (fn [new-exec-args] + (assoc new-exec-args k (get exec-args k default)))))) + {} + spec))) (defn parse-cmds "Parses sub-commands (arguments not starting with an option prefix) and returns a map with: @@ -290,7 +291,7 @@ opts (if spec (merge-opts opts - (spec->opts spec)) + (spec->opts spec opts)) opts) coerce-opts (:coerce opts) aliases (or diff --git a/test/babashka/cli_test.cljc b/test/babashka/cli_test.cljc index 48fe979..fc6f298 100644 --- a/test/babashka/cli_test.cljc +++ b/test/babashka/cli_test.cljc @@ -195,7 +195,7 @@ :to :keyword, :paths []}, :alias {:i :from, :o :to, :p :pretty}, :exec-args {:from :edn, :to :json, :paths ["src" "test"]}} - (cli/spec->opts spec))) + (cli/spec->opts spec nil))) (is (= (str/trim " -p, --pretty Pretty-print output. --paths src test Paths of files to transform.