Example for custom Cmdliner arguments

Cmdliner supports argument values like bool, int, dir or file but these can be extended with custom types. I would be interested in an example for this. For example, I’d like to support min:sec durations or ISO date-time strings. One needs to provide a parser and a printer for each. Conceptually this is not difficult but I struggle with the maze of types in cmdliner.mli.

The loc_arg in this sample code of the documentation shows this. HTH.

1 Like

From the documentation mentioned:

let loc_arg =
  let parse s =
    try
      if s <> "" && s.[0] <> '+'
      then Ok (true, int_of_string s)
      else Ok (false, int_of_string (String.sub s 1 (String.length s - 1)))
    with Failure _ -> Error (`Msg "unable to parse integer")
  in
  let print ppf p = Format.fprintf ppf "%s" (loc_str p) in
  Arg.conv ~docv:"N" (parse, print)

My own converter for min:sec values:


  open Rresult
  module C = Cmdliner

  let msg fmt = Printf.ksprintf (fun str -> `Msg str) fmt

  let min_sec =
    let parse str =
      try R.ok (Scanf.sscanf str "%u:%u" (fun min sec -> (min * 60) + sec))
      with _ -> R.error (msg "Can't parse %s as min:sec value" str)
    in
    let print ppf sec = Format.fprintf ppf "%d:%0d" (sec / 60) (sec mod 60) in
    C.Arg.conv ~docv:"S" (parse, print)

  let duration =
    C.Arg.(
      value & opt min_sec 30
      & info [ "d"; "duration" ] ~docv:"DURATION"
          ~doc:"Duration in min:sec of video (and data to extract)")

A good way to find example uses of this or other API is sherlocode:

Sherlocode(%5C%3Fconv%20

EDIT: this markdown parser struggles with the URL so here it is: https://sherlocode.com/?q=Arg.(%5C%3Fconv%20

3 Likes

Would it be of interest to add more (extra) command line types to Cmdliner that could be provided by the community @dbuenzli? I could imagine:

  • date and time values as discussed above
  • 2^n value like 1k, 3M (or 1kib)

You probably want to avoid introducing new dependencies just for this, which eliminates a complex syntax like an URI or similar.

I don’t think these should belong in the cmdliner package (to avoid introducing additional dependencies to everyone). But either of the two following proposals could make sense:

  • start opening PRs on your library foo (be it for calendar, colour, geometry, or whathaveyou) to add a new package foo-cmdliner in the repo
  • start a cmdliner-convs project which publishes several small packages: one for each library you want convs for
3 Likes

In general I already find cmdliner to be too much code for what it’s doing, so I rather not add too much there. Especially since this code will end up in each executable even if not used.

The problem for date, time an durations is that there’s no unique answer on how to represent them. For specifying bytes amount it’s a bit less (but still, an int, an int64 ?) and then there’s the problem of SI prefixes vs SI binary prefixes.

So for the time being I find @raphael-proust’s answer more adequate :–)