diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ce1376..0a9c1e5 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 + +- [#116](https://github.com/babashka/cli/issues/116): Undeprecate `:collect` option to support custom transformation of arguments to collections + ## v0.8.62 (2024-12-22) - Fix [#109](https://github.com/babashka/cli/issues/109): allow options to start with a number diff --git a/README.md b/README.md index e6eb807..3c32d53 100644 --- a/README.md +++ b/README.md @@ -208,6 +208,19 @@ Arguments that start with `--no-` arg parsed as negative flags (since 0.7.51): ;;=> {:colors false} ``` +### Custom collection handling + +Usually the above will suffice, but for custom transformation to a collection, you can use `:collect`. +Here's an example of parsing out `,` separated multi-arg-values: + +``` clojure +(cli/parse-opts ["--foo" "a,b" "--foo=c,d,e" "--foo" "f"] + {:collect {:foo (fn [coll arg-value] + (into (or coll []) + (str/split arg-value #",")))}}) +;; => {:foo ["a" "b" "c" "d" "e" "f"]} +``` + ### Auto-coercion Since `v0.3.35` babashka CLI auto-coerces values that have no explicit coercion @@ -430,6 +443,7 @@ An explanation of each key: - `:require`: `true` make this opt required. - `:validate`: a function used to validate the value of this opt (as described in the [Validate](#validate) section). +- `:collect`: for custom collection/transformation of argument values ## Help diff --git a/src/babashka/cli.cljc b/src/babashka/cli.cljc index 3d7c26b..6e30652 100644 --- a/src/babashka/cli.cljc +++ b/src/babashka/cli.cljc @@ -282,6 +282,7 @@ * `:exec-args` - a map of default args. Will be overridden by args specified in `args`. * `:no-keyword-opts` - `true`. Support only `--foo`-style opts (i.e. `:foo` will not work). * `:args->opts` - consume unparsed commands and args as options + * `:collect` - a map of collection fns. See [custom collection handling](https://github.com/babashka/cli#custom-collection-handling). Examples: diff --git a/test/babashka/cli_test.cljc b/test/babashka/cli_test.cljc index 2b0a119..59700f7 100644 --- a/test/babashka/cli_test.cljc +++ b/test/babashka/cli_test.cljc @@ -172,7 +172,10 @@ :coerce {:verbose []}})))) (deftest spec-test - (let [spec {:from {:ref "" + (let [multi-arg-val-collect (fn [coll arg-value] + (into (or coll []) + (str/split arg-value #":"))) + spec {:from {:ref "" :desc "The input format. can be edn, json or transit." :coerce :keyword :alias :i @@ -189,18 +192,23 @@ :paths {:desc "Paths of files to transform." :coerce [] :default ["src" "test"] - :default-desc "src test"}}] + :default-desc "src test"} + :multi {:desc "Custom multi-arg-val test." + :alias :m + :collect multi-arg-val-collect}}] (is (= (str/trim " -i, --from edn The input format. can be edn, json or transit. -o, --to json The output format. can be edn, json or transit. --paths src test Paths of files to transform. - -p, --pretty Pretty-print output.") + -p, --pretty Pretty-print output. + -m, --multi Custom multi-arg-val test.") (str/trim (cli/format-opts {:spec spec - :order [:from :to :paths :pretty]})))) + :order [:from :to :paths :pretty :multi]})))) (is (= {:coerce {:from :keyword, :to :keyword, :paths []}, - :alias {:i :from, :o :to, :p :pretty}, - :exec-args {:from :edn, :to :json, :paths ["src" "test"]}} + :alias {:i :from, :o :to, :p :pretty :m :multi}, + :exec-args {:from :edn, :to :json, :paths ["src" "test"]} + :collect {:multi multi-arg-val-collect}} (cli/spec->opts spec nil))) (is (= (str/trim " -p, --pretty false Pretty-print output. @@ -213,6 +221,11 @@ :coerce [] :default ["src" "test"] :default-desc "src test"}]]})))) + (is (submap? + {:opts {:from :edn :to :json :paths ["src" "test"] + :multi ["a" "b" "c" "d" "e" "f" "g" "h"]}} + (cli/parse-args ["--multi" "a:b" "--multi" "c:d:e" "--multi" "f" "--multi" "g" "h"] + {:spec spec}))) (is (submap? {:opts {:from :edn, :to :json, :paths ["src" "test"]}} (cli/parse-args [] {:spec spec}))) @@ -222,6 +235,7 @@ #:deps{:root "the-root"} (cli/parse-opts ["--deps/root" "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}}))))