Welcome! Please see the About page for a little more info on how this works.

+1 vote
in tools.cli by

Hi,

A very simplified tools.cli call:

(let [cli-opts [["-m" "--mode MODE" "Application mode"]]]
  (cli/parse-opts ["-m" "hub"] cli-opts)) 

Evaluates as expected to:

{:options {:mode "hub"} ... }

However, when I try to replace the long option with nil per the docs:

(let [cli-opts [["-m" nil "Application mode" :id :mode]]]
  (cli/parse-opts ["-m" "hub"] cli-opts))

I get:

{:options {:mode true} :arguments ["hub"] ... }

I get the same behaviour with long option = "", "--mode", and "MODE"; the only way it seems to work is with the full "--mode MODE". Setting the short option to nil works as I'd expect.

Tested in Clojure 1.12.0, tools.cli 1.1.230, Java 23; same behaviour on Windows 11 and MacOS 15.

Am I doing something wrong, or is tools.cli? :)

2 Answers

0 votes
by
selected by
 
Best answer

Try adding :required "MODE" after :id :mode.

When there's no long option in the form of --option ARG, the parameter is treated as a boolean flag, so you have to explicitly tell tools.cli that an argument is required by the parameter.

by
Hi Eugene,

Thanks for this. It looks like {:required <any truthy value>} works. However, surprisingly, this doesn't actually make the option required - omitting the param still results in {... :errors nil})!
by
`:required` doesn't meant that the option is required. It specifies that the option requires an argument, and the name of that argument. It's all in the docstring of `clojure.tools.cli/parse-opts`.
by
The docstring and the readme have been updated. A new release will get cut at some point, but that's almost the only change since the last release so it might be a while.
0 votes
by
edited by

As pointed out in the comments, this is an incorrect answer. Even as maintainer of tools.cli, I misunderstood the implications of the documentation!


The required argument example can only be part of a long option. When you have no long option, it is assumed to be a Boolean switch argument. It is not possible to have a required argument without having a long option.

That's what the following in the README is meant to convey:

   ;; If no required argument description is given, the option is assumed to
   ;; be a Boolean option defaulting to nil
   [nil "--detach" "Detach from controlling process"]
   ["-v" nil "Verbosity level; may be specified multiple times to increase value"
    ;; If no long-option is specified, an option :id must be given
    :id :verbosity
    :default 0
    ;; Use :update-fn to create non-idempotent options (:default is applied first)
    :update-fn inc]

I'll see if I can make that clearer in the usage notes.

by
But it is possible, via `:required` - see my answer below.
by
Hi Sean,

Thanks for the quick and detailed response! I understand the issue now. However, some of this — specifically the existence and behaviour of the :required key, and how it is inferred from the long option — still feels unintuitive to me (perhaps because I've never come across the Argument Syntax Convention before!).

Given that providing nil for the short option works as expected, I agree that clarification would be helpful; the docstring for :required explains the behaviour well enough, so surfacing that in the readme might be sufficient?

Thanks again for all your work on this (and other projects)!
...