Coder Social home page Coder Social logo

hammerlab / ppx_deriving_cmdliner Goto Github PK

View Code? Open in Web Editor NEW
96.0 9.0 12.0 65 KB

Ppx_deriving plugin for generating command line interfaces from types (Cmdliner.Term.t)

License: Apache License 2.0

Makefile 0.96% OCaml 99.04%
ocaml cli command-line-parser ppx ppx-extension

ppx_deriving_cmdliner's Introduction

[@@deriving cmdliner]

deriving Cmdliner is the easiest way to get a command line interface.

It is also a ppx_deriving plugin that generates a Cmdliner Term for a given type.

example workflow

Example

type params = {
  username: string;
  (** Your Github username *)

  api_key: string;
  (** Your Github API key *)

  command: string; [@pos 0] [@docv "CMD"]
  (** The Github API command to run *)

  dry_run: bool;
  (** Don't really run this command *)

  time_to_wait: float; [@default 0.]
  (** Just an example of another type *)
} [@@deriving cmdliner, show]

let main () =
  let f p = show_params p |> print_endline in
  let info = Cmdliner.Cmd.info Sys.argv.(0) in
  let term = Cmdliner.Term.(const f $ params_cmdliner_term ()) in
  let cmd = Cmdliner.Cmd.v info term in
  exit (Cmdliner.Cmd.eval cmd)

let () = main ()

Which gives you a CLI like the following:

NAME
       awesome-cli

SYNOPSIS
       awesome-cli [OPTION]... CMD

ARGUMENTS
       CMD (required)
            The Github API command to run

OPTIONS
       --api-key=STRING (required)
            Your Github API key

       --dry-run
            Don't really run this command

       --help[=FMT] (default=auto)
           Show this help in format FMT. The value FMT must be one of `auto',
           `pager', `groff' or `plain'. With `auto', the format is `pager` or
           `plain' whenever the TERM env var is `dumb' or undefined.

       --time-to-wait=FLOAT (absent=0.)
            Just an example of another type

       --username=STRING (required)
            Your Github username

Features

Custom type support

Ppx_deriving_cmdliner supports arbitrary types via a cmdliner_converter interface. For example, the below two methods work for supporting M.t (from test/tests.ml)

module M = struct
  type t = int * int
  let fst (f,_) = f
  let snd (_,s) = s
  let of_string s =
    try
      let sepi = String.index s '|' in
      let fst = String.sub s 0 sepi in
      let snd = String.sub s (sepi+1) ((String.length s)-sepi-1) in
      Result.Ok (int_of_string fst, int_of_string snd)
    with _ -> Result.Error (`Msg (Printf.sprintf "Couldn't parse `%s`" s))
  let to_string t =
    Printf.sprintf "%d|%d" (fst t) (snd t)
  let cmdliner_converter =
    of_string,
    (fun fmt t -> Format.fprintf fmt "%s" (to_string t))
end
type custom_types = {
  foo: M.t; [@conv M.cmdliner_converter]
  bar: M.t;
} [@@deriving cmdliner]

In short, a value of type string -> ('a, [ `Msg of string ]) Result.result) * 'a printer must be provided (or will be looked for under the name cmdliner_converter if the type is t, else type_name_cmdliner_converter) for the given type.

Attributes supported

  1. Docs: [@doc "Overwrites the docstring"], [@docs "SECTION TWO"], [@docv "VAL"]
  2. Environment variables: [@env "ENVNAME"], [@env.doc "Docs for the variable"], [@env.docs "SECTION ENVS"]
  3. Other: [@list_sep '@'], [@default 123], [@enum [("a", Foo); ("b", Bar)]], [@aka ["b";"another-flag-name"]], [@conv cmdliner_converter] (cf. required argument to conv in Cmdliner), [@opt_all] only on a' list fields, [@term cmdliner_term] for assiging an arbitrary Cmdliner.Term.t to a field.

ppx_deriving_cmdliner's People

Contributors

armish avatar constfun avatar ihodes avatar jeffa5 avatar mseri avatar pitag-ha avatar smondet avatar trepetti avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ppx_deriving_cmdliner's Issues

Support variants (for enums)

We already support @enum syntax, but a simple variant type foo = Bar | Baz | Qux in a

{ foo: foo } should default to Cmdliner.Arg.(required & opt (some (enum [(Bar, "bar"; …)])) & info ["foo"])

from @smondet:

Another way could be:

type t = {
  (* ... *)
  my_option: [ `One | `Two [@name "second"] | `Three ] [@as_enum];
  (* ... *)
]

that makes a Cmdliner enum: [ `One, "one"; `Two, "second"; `Three, "three" ]

Support variants

module C = struct
  type file = string

  type t =
    | Str of string
    | File of file
end

type params = {
  c: C.t option; [@without_prefix]
  other_c: C.t option; [@with_prefix "another-c"]
} [@deriving cmdliner]

`[@enum ...]` ignored without error message or warning

Given the semantics of ppx annotations maybe this is just normal; but putting an issue to remember it.

  type t = {
    dry_run:
      bool
        [@default false]
        [@enum [("yes", true); ("no", false)]];
    (** Whether to actually do things, or not. *)
  } [@@deriving cmdliner,show]

The [@enum ...] part is completely ignored.


OPTIONS
       --dry-run
            Whether to actually do things, or not. 

Create "the main entry point" `[@@cmdliner.eval params]`

Not sure this is a good idea; but this could simplify simple cases further.

Like in the example:

type params = {
   (* ... *)
} [@@deriving cmdliner]
let my_main params =
   (* do stuff *)
let () =
  let term = Cmdliner.Term.(const my_main $ params_cmdliner_term ()) in
  let info = Cmdliner.Term.info Sys.argv.(0) in
  Cmdliner.Term.eval (term, info)

could be:

type params = {
   (* ... *)
} [@@deriving cmdliner]
let my_main params [@@deriving.eval_with params] =
   (* do stuff *)

and the let () would be generated.

jbuilder/dune support?

Can I build programs using ppx_deriving_cmdliner with jbuilder? Adding

(preprocess (pps (ppx_deriving_cmdliner)))

does not seem to work.

Official `opam` file points at wrong URLs

They all point at github.com/ihodes/:

https://github.com/ocaml/opam-repository/pull/8667/files

$ opam info ppx_deriving_cmdliner
             package: ppx_deriving_cmdliner
             version: 0.0.0
          repository: default
        upstream-url: https://github.com/hammerlab/ppx_deriving_cmdliner/archive/v0.0.0.tar.gz
       upstream-kind: http
   upstream-checksum: 976fd6fac87489ad76f81f10329fe1b8
            homepage: https://github.com/ihodes/ppx_deriving_cmdliner
         bug-reports: https://github.com/ihodes/ppx_deriving_cmdliner/issues
            dev-repo: https://github.com/ihodes/ppx_deriving_cmdliner.git
              author: Isaac Hodes <[email protected]>
             license: MIT
                 doc: http://ihodes.github.io/ppx_deriving_cmdliner
                tags: syntax, cli
             depends: cmdliner & result & ppx_deriving (>= 4.0 & < 5.0) & ocamlfind & cppo
   installed-version: 0.0.0 [4.03.0]
   available-version: 0.0.0
         description: Cmdliner.Term.t generator

ppx_deriving_cmdliner is a ppx_deriving plugin that generates
Cmdliner Terms for types.

New release with Dune 2.0 support?

Hi, thank you for this awesome tool! Is it possible to do a new release with Dune 2.0 support? (looks like everything is in place now, unless I'm missing something)

List enum value in help, optionally.

Like cmdliner, we don't list enum values in the help:

ocaml# type t = { foo: string [@enum ["a", "b"]] [@docv "FOO"];(** Hello *) } [@@deriving cmdliner];;
type t = { foo : string; }
val cmdliner_term : unit -> t Cmdliner.Term.t = <fun>
ocaml# Cmdliner.Term.(eval ~argv:[| "hello"; "--help=plain" |] (cmdliner_term (), info "hello"));;
NAME
        hello
SYNOPSIS
       hello [OPTION]... 

OPTIONS
       --foo=FOO (required)
            Hello 

       --help[=FMT] (default=auto)
           Show this help in format FMT. The value FMT must be one of `auto',
           `pager', `groff' or `plain'. With `auto', the format is `pager` or
           `plain' whenever the TERM env var is `dumb' or undefined.

- : t Cmdliner.Term.result = `Help

It'd be cool to have an option

type t = {
   foo: string [@enum ["a", "b"]] [@docv "FOO"] [@cmdliner.list_enum];
   (** Some help *)
} [@@deriving cmdliner];;

Scope of generated `[@ocaml.warning "-A"]` too narrow

In the code generated, such as:

let rec (cmdliner_term : unit -> t Cmdliner.Term.t) =
  ((let open! Ppx_deriving_cmdliner_runtime in
  ...)
  [@ocaml.warning "-A"])

the warning attribute does not disable Warning 39: unused rec flag. on the first line.

Strip doc strings to preserve alignment

The strings in (** .... *) comments should be space-stripped:

  type t = {
    tags : string list [@default []] [@ocaml.doc "Some doc"];

    tagz : string list [@default []];
    (** Some doc *)

gives in --help:

       --help[=FMT] (default=auto)
           Show this help in format FMT. The value FMT must be one of `auto',
           `pager', `groff' or `plain'. With `auto', the format is `pager` or
           `plain' whenever the TERM env var is `dumb' or undefined.

       --tags=STRING,...
           Some doc

       --tagz=STRING,...
            Some doc 

We see that the 'S' of the --tags one is well aligned with the 'S' of the
--help one but not with the --tagz one.

Positional arguments with option type crash cmdliner

If you specify [@pos] on a field that is an option type, cmdliner will crash with Fatal error: exception Invalid_argument("Option argument without name")

I tried digging into this but I couldn't see where the issue would be.

Default parameters

Hello,
I am trying to integrate default behaviour into my main program. When it is run in a terminal without any terminal commands I want the manpage to be printed. Is this kind of default behaviour already given in a function in ppx_deriving_cmdliner or is this an issue for the package cmdliner only?

"include" for terms

Copying from old issue @smondet

common : Common.t [@with_term]
(** The record has a `common` field but doesn't create `--common` options; it
take the whole `Common.term` and puts it inline. *)

or

type cmd = {
  not_common: int;
  not_common_either: int;
  [%include some_arg_or_term_definition;
} [@@deriving to_cmdliner,show]

Publish new version to opam?

The latest version in opam is v0.6.0, published in May 2021. It depends on ppxlib < 0.26.0. Many other packages require a newer version of ppxlib at this point, which means that attempting to install ppx_deriving_cmdliner alongside them leads to a conflict.

I see that this dependency constraint has been relaxed in recent versions. Would a maintainer be willing to publish a new version to opam to address this problem?

0.6.0 does not compile

Hey,
whenever I try to update ppx_deriving_cmdliner, I get the following error:

#=== ERROR while compiling ppx_deriving_cmdliner.0.6.0 ========================#
# context     2.1.0~rc2 | linux/x86_64 | ocaml-base-compiler.4.11.2 | https://opam.ocaml.org#d3b8ae21
# path        ~/.opam/4.11.2/.opam-switch/build/ppx_deriving_cmdliner.0.6.0
# command     ~/.opam/opam-init/hooks/sandbox.sh build dune build -p ppx_deriving_cmdliner -j 11
# exit-code   1
# env-file    ~/.opam/log/ppx_deriving_cmdliner-40672-21de72.env
# output-file ~/.opam/log/ppx_deriving_cmdliner-40672-21de72.out
### output ###
#       ocamlc src/.ppx_deriving_cmdliner.objs/byte/ppx_deriving_cmdliner.{cmi,cmo,cmt} (exit 2)
# (cd _build/default && $HOME/.opam/4.11.2/bin/ocamlc.opt -w -40 -w -9-16-27-39 -g -bin-annot -I src/.ppx_deriving_cmdliner.objs/byte -I $HOME/.opam/4.11.2/lib/ocaml-compiler-libs/common -I $HOME/.opam/4.11.2/lib/ocaml-compiler-libs/shadow -I $HOME/.opam/4.11.2/lib/ocaml-migrate-parsetree -I $HOME/.opam/4.11.2/lib/ocaml/compiler-libs -I $HOME/.opam/4.11.2/lib[...]
# File "src/ppx_deriving_cmdliner.ml", line 313, characters 39-77:
# 313 |         let e_type_path = Exp.constant (Pconst_string (type_path, loc, None)) in
#                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Error: The constructor Pconst_string expects 2 argument(s),
#        but is applied here to 3 argument(s)

The opam packages installed on my systems are the following:

dune                    2.9.0        
ocaml                   4.11.2       
ocaml-base-compiler     4.11.2      
ocaml-compiler-libs     v0.12.3      
ocaml-config            1           
ocaml-migrate-parsetree 1.8.0        
ocamlbuild              0.14.0       
ocamlfind               1.9.1       
ppx_deriving            4.5          
ppxlib                  0.15.0

Does anybody know how to resolve this problem? The underlying system is Ubuntu 20.04.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.