The old interface for command-line specifications -- Do Not Use.
This interface should not be used. See the Param
module for
the new way to do things.
include Param.S with type a t := a param
Command.Param
is intended to be used with the [%map_open]
syntax defined in
ppx_let
, like so:
let command =
Command.basic ~summary:"..."
[%map_open
let count = anon ("COUNT" %: int)
and port = flag "port" (optional int) ~doc:"N listen on this port"
and person = person_param
in
(* ... Command-line validation code, if any, goes here ... *)
fun () ->
(* The body of the command *)
do_stuff count port person
]
One can also use [%map_open]
to define composite command line parameters, like
person_param
in the previous snippet:
type person = { name : string; age : int }
let person_param : person Command.Param.t =
[%map_open
let name = flag "name" (required string) ~doc:"X name of the person"
and age = flag "age" (required int) ~doc:"N how many years old"
in
{name; age}
]
The right-hand sides of [%map_open]
definitions have Command.Param
in scope.
Alternatively, you can say:
let open Foo.Let_syntax in
[%map_open
let x ...
]
if Foo
follows the same conventions as Command.Param
.
See example/command/main.ml for more examples.
include Core__.Import.Applicative.S with type a t := a t
val return : 'a ‑> 'a t
module Applicative_infix : sig ... end
val flag : ?aliases:string list ‑> ?full_flag_required:unit ‑> string ‑> 'a Flag.t ‑> doc:string ‑> 'a t
flag name spec ~doc
specifies a command that, among other things, takes a flag
named name
on its command line. doc
indicates the meaning of the flag.
All flags must have a dash at the beginning of the name. If name
is not
prefixed by "-", it will be normalized to "-" ^ name
.
Unless full_flag_required
is used, one doesn't have to pass name
exactly on
the command line, but only an unambiguous prefix of name
(i.e., a prefix which
is not a prefix of any other flag's name).
NOTE: the doc
for a flag which takes an argument should be of the form
arg_name ^ " " ^ description
where arg_name
describes the argument and
description
describes the meaning of the flag.
NOTE: flag names (including aliases) containing underscores will be rejected. Use dashes instead.
NOTE: "-" by itself is an invalid flag name and will be rejected.
val flag_optional_with_default_doc : ?aliases:string list ‑> ?full_flag_required:unit ‑> string ‑> 'a Arg_type.t ‑> ('a ‑> Core__.Import.Sexp.t) ‑> default:'a ‑> doc:string ‑> 'a t
flag_optional_with_default_doc name arg_type sexp_of_default ~default ~doc
is a
shortcut for flag
, where:
Flag.t
is optional_with_default default arg_type
doc
is passed through with an explanation of what the default value
appended.type (-'main_in, +'main_out) t
Composable command-line specifications.
Ultimately one forms a basic command by combining a spec of type ('main, unit ->
unit) t
with a main function of type 'main
; see the basic
function below.
Combinators in this library incrementally build up the type of main according to
what command-line parameters it expects, so the resulting type of main
is
something like:
arg1 -> ... -> argN -> unit -> unit
It may help to think of ('a, 'b) t
as a function space 'a -> 'b
embellished with
information about:
One can view a value of type ('main_in, 'main_out) t
as a function that transforms
a main function from type 'main_in
to 'main_out
, typically by supplying some
arguments. E.g., a value of type Spec.t
might have type:
(arg1 -> ... -> argN -> 'r, 'r) Spec.t
Such a value can transform a main function of type arg1 -> ... -> argN -> 'r
by
supplying it argument values of type arg1
, ..., argn
, leaving a main function
whose type is 'r
. In the end, Command.basic
takes a completed spec where
'r = unit -> unit
, and hence whose type looks like:
(arg1 -> ... -> argN -> unit -> unit, unit -> unit) Spec.t
A value of this type can fully apply a main function of type arg1 -> ... -> argN ->
unit -> unit
to all its arguments.
The final unit argument allows the implementation to distinguish between the phases of (1) parsing the command line and (2) running the body of the command. Exceptions raised in phase (1) lead to a help message being displayed alongside the exception. Exceptions raised in phase (2) are displayed without any command line help.
The view of ('main_in, main_out) Spec.t
as a function from 'main_in
to
'main_out
is directly reflected by the step
function, whose type is:
val step : ('m1 -> 'm2) -> ('m1, 'm2) t
spec1 ++ spec2 ++ ... ++ specN
composes spec1
through specN
.
For example, if spec_a
and spec_b
have types:
spec_a: (a1 -> ... -> aN -> 'ra, 'ra) Spec.t;
spec_b: (b1 -> ... -> bM -> 'rb, 'rb) Spec.t
then spec_a ++ spec_b
has the following type:
(a1 -> ... -> aN -> b1 -> ... -> bM -> 'rb, 'rb) Spec.t
So, spec_a ++ spec_b
transforms a main function by first supplying spec_a
's
arguments of type a1
, ..., aN
, and then supplying spec_b
's arguments of type
b1
, ..., bm
.
One can understand ++
as function composition by thinking of the type of specs
as concrete function types, representing the transformation of a main function:
spec_a: \/ra. (a1 -> ... -> aN -> 'ra) -> 'ra;
spec_b: \/rb. (b1 -> ... -> bM -> 'rb) -> 'rb
Under this interpretation, the composition of spec_a
and spec_b
has type:
spec_a ++ spec_b : \/rc. (a1 -> ... -> aN -> b1 -> ... -> bM -> 'rc) -> 'rc
And the implementation is just function composition:
sa ++ sb = fun main -> sb (sa main)
Adds a leftmost parameter onto the type of main.
This function should only be used as a workaround in situations where the order of composition is at odds with the order of anonymous arguments because you're factoring out some common spec.
val step : ('m1 ‑> 'm2) ‑> ('m1, 'm2) t
Combinator for patching up how parameters are obtained or presented.
Here are a couple examples of some of its many uses:
introducing labeled arguments
step (fun m v -> m ~foo:v) +> flag "-foo" no_arg : (foo:bool -> 'm, 'm) t
prompting for missing values
step (fun m user -> match user with | Some user -> m user | None -> print_string "enter username: "; m (read_line ())) +> flag "-user" (optional string) ~doc:"USER to frobnicate" : (string -> 'm, 'm) t
A use of step
might look something like:
step (fun main -> let ... in main x1 ... xN) : (arg1 -> ... -> argN -> 'r, 'r) t
Thus, step
allows one to write arbitrary code to decide how to transform a main
function. As a simple example:
step (fun main -> main 13.) : (float -> 'r, 'r) t
This spec is identical to const 13.
; it transforms a main function by supplying
it with a single float argument, 13.
. As another example:
step (fun m v -> m ~foo:v) : (foo:'foo -> 'r, 'foo -> 'r) t
This spec transforms a main function that requires a labeled argument into a main function that requires the argument unlabeled, making it easily composable with other spec combinators.
Combinator for defining a class of commands with common behavior.
Here are two examples of command classes defined using wrap
:
print top-level exceptions to stderr
wrap (fun ~run ~main -> Exn.handle_uncaught ~exit:true (fun () -> run main) ) : ('m, unit) t -> ('m, unit) t
iterate over lines from stdin
wrap (fun ~run ~main -> In_channel.iter_lines stdin ~f:(fun line -> run (main line)) ) : ('m, unit) t -> (string -> 'm, unit) t
module Arg_type : module type of Arg_type with type 'a Arg_type.t = 'a Arg_type.t
include module type of Arg_type.Export
val string : string Arg_type.t
val int : int Arg_type.t
Beware that an anonymous argument of type int
cannot be specified as negative,
as it is ambiguous whether -1 is a negative number or a flag. (The same applies
to float
, time_span
, etc.) You can use the special built-in "-anon" flag to
force a string starting with a hyphen to be interpreted as an anonymous argument
rather than as a flag, or you can just make it a parameter to a flag to avoid the
issue.
val char : char Arg_type.t
val float : float Arg_type.t
val bool : bool Arg_type.t
val date : Core__.Import.Date.t Arg_type.t
val percent : Core__.Import.Percent.t Arg_type.t
val time : Core__.Import_time.Time.t Arg_type.t
val time_ofday_unzoned : Core__.Import_time.Time.Ofday.t Arg_type.t
For when the time zone is implied.
val time_zone : Core__.Import_time.Time.Zone.t Arg_type.t
val time_span : Core__.Import_time.Time.Span.t Arg_type.t
val host_and_port : Core__.Import.Host_and_port.t Arg_type.t
val ip_address : Unix.inet_addr Arg_type.t
val sexp : Core__.Import.Sexp.t Arg_type.t
val sexp_conv : (Core__.Import.Sexp.t ‑> 'a) ‑> 'a Arg_type.t
include module type of Flag with type a Flag.t := a flag
Command-line flag specifications.
val optional_with_default : 'a ‑> 'a Arg_type.t ‑> 'a t
optional_with_default
flags may be passed at most once, and default to a given
value.
val one_or_more : 'a Arg_type.t ‑> ('a * 'a list) t
one_or_more
flags must be passed one or more times.
val no_arg : bool t
no_arg
flags may be passed at most once. The boolean returned is true iff the
flag is passed on the command line.
val no_arg_register : key:'a Core__.Import.Univ_map.With_default.Key.t ‑> value:'a ‑> bool t
no_arg_register ~key ~value
is like no_arg
, but associates value
with key
in
the autocomplete environment.
val no_arg_abort : exit:(unit ‑> Core__.Import.never_returns) ‑> unit t
no_arg_abort ~exit
is like no_arg
, but aborts command-line parsing by calling
exit
. This flag type is useful for "help"-style flags that just print something
and exit.
val escape : string list option t
escape
flags may be passed at most once. They cause the command line parser to
abort and pass through all remaining command line arguments as the value of the
flag.
A standard choice of flag name to use with escape
is "--"
.
val flags_of_args_exn : Core_kernel.Arg.t list ‑> ('a, 'a) t
flags_of_args_exn args
creates a spec from Caml.Arg.t
s, for compatibility with
OCaml's base libraries. Fails if it encounters an arg that cannot be converted.
NOTE: There is a difference in side effect ordering between Caml.Arg
and
Command
. In the Arg
module, flag handling functions embedded in Caml.Arg.t
values will be run in the order that flags are passed on the command line. In the
Command
module, using flags_of_args_exn flags
, they are evaluated in the order
that the Caml.Arg.t
values appear in flags
.
include module type of Anons with type a Anons.t := a anons
Anonymous command-line argument specification.
val (%:) : string ‑> 'a Arg_type.t ‑> 'a t
(name %: typ)
specifies a required anonymous argument of type typ
.
The name
must not be surrounded by whitespace; if it is, an exn will be raised.
If the name
is surrounded by a special character pair (<>, {}, [] or (),)
name
will remain as-is, otherwise, name
will be uppercased.
In the situation where name
is only prefixed or only suffixed by one of the
special character pairs, or different pairs are used (e.g., "<ARG]"), an exn will
be raised.
The (possibly transformed) name
is mentioned in the generated help for the
command.
sequence anons
specifies a sequence of anonymous arguments. An exception will be
raised if anons
matches anything other than a fixed number of anonymous arguments.
non_empty_sequence_as_pair anons
and non_empty_sequence_as_list anons
are like
sequence anons
except that an exception will be raised if there is not at least
one anonymous argument given.
t2
, t3
, and t4
each concatenate multiple anonymous argument specs into a
single one. The purpose of these combinators is to allow for optional sequences of
anonymous arguments. Consider a command with usage:
main.exe FOO [BAR BAZ]
where the second and third anonymous arguments must either both be there or both not be there. This can be expressed as:
t2 ("FOO" %: foo) (maybe (t2 ("BAR" %: bar) ("BAZ" %: baz)))]
Sequences of 5 or more anonymous arguments can be built up using nested tuples:
maybe (t3 a b (t3 c d e))
Conversions to and from new-style Param
command line specifications.