Есть ли способ сгруппировать аргументы в записи при использовании Cmdliner? - PullRequest
1 голос
/ 28 января 2020

Допустим, у меня есть следующий код

let main a b c d e = Format.eprintf "%B %B %B %B %B@." a b c d e

let cmd =
  let open Cmdliner in
  let a = Arg.(value & flag & info ["a"] ~doc:"a") in
  let b = Arg.(value & flag & info ["b"] ~doc:"b") in
  let c = Arg.(value & flag & info ["c"] ~doc:"c") in
  let d = Arg.(value & flag & info ["d"] ~doc:"d") in
  let e = Arg.(value & flag & info ["e"] ~doc:"e") in

  Term.(const main $ a $ b $ c $ d $ e), Term.(info "test" ~version:"1" ~doc:"abcde" ~exits:default_exits ~man:[])

let () = Cmdliner.Term.(exit @@ eval cmd)

Если я выполню свою программу без параметров, я получу false false false false false, а если я использую ее с -ade, я получу true false false true true, что именно то, что я хотел.

Теперь предположим, что я сделал опечатку в своей функции main и написал вместо нее

(* Notice the d before c *)
let main a b d c e = Format.eprintf "%B %B %B %B %B@." a b c d e

Если я выполню свою основную программу с -ade, как ранее, я буду получить true false true false true, что можно считать неправильным.

Итак, я хотел бы знать, можно ли собрать в записи параметры для использования их с собственными именами, что-то вроде следующего примера (который не не работает):

open Cmdliner

type o = {a : bool Term.t;
          b : bool Term.t;
          c : bool Term.t;
          d : bool Term.t;
          e : bool Term.t;}

(* a - e are not booleans but bool Term.t which gives an obvious error *)
let main {a; b; c; d; e} = Format.eprintf "%B %B %B %B %B@." a b c d e

let cmd =
  let a = Arg.(value & flag & info ["a"] ~doc:"a") in
  let b = Arg.(value & flag & info ["b"] ~doc:"b") in
  let c = Arg.(value & flag & info ["c"] ~doc:"c") in
  let d = Arg.(value & flag & info ["d"] ~doc:"d") in
  let e = Arg.(value & flag & info ["e"] ~doc:"e") in

  let o = Term.const {a; b; c; d; e} in
  Term.(const main $ o), Term.(info "test" ~version:"1" ~doc:"abcde" ~exits:default_exits ~man:[])

let () = Cmdliner.Term.(exit @@ eval cmd)

Это может быть полезно в больших проектах и ​​уменьшит количество аргументов, переданных функциям. Возможно, есть способ сделать это, но все примеры, которые я нашел, использовали первый способ. Я не хотел открывать проблему на странице GitHub, поэтому я спросил ее здесь.

1 Ответ

2 голосов
/ 28 января 2020

Это можно сделать довольно напрямую, если вы напишите функции обновления поля для типа записи. Например, если у нас есть

type arg = { a:bool; b:bool; c:bool; d:bool; e: bool }

let main {a;b;c;d;e} = Format.eprintf "%B %B %B %B %B@." a b c d e

module Update = struct
  let a a r = { r with a }
  let b b r = { r with b }
  let c c r = { r with c }
  let d d r = { r with d }
  let e e r = { r with e }
end

Единственный пропущенный шаг - это преобразование Cmdliner.Term.t, которое напрямую предоставляет аргумент, в термины, которые обновляют запись типа arg. Реализация будет:

let cmd =
  let open Cmdliner in
  (* first the starting record *)
  let start = Term.const { a = false; b=false; c=false; d=false; e=false } in
  let transform r (update,arg) = 
    Term.( const update $ arg $ r ) in
  let arg =
    List.fold_left transform
      start
      Update.[
        a, Arg.(value & flag & info ["a"] ~doc:"a");
        b, Arg.(value & flag & info ["b"] ~doc:"b");
        c, Arg.(value & flag & info ["c"] ~doc:"c");
        d, Arg.(value & flag & info ["d"] ~doc:"d");
        e, Arg.(value & flag & info ["e"] ~doc:"e");
    ] in
  Term.(const main $ arg),
  Term.info "test"
     ~version:"1"
     ~doc:"abcde"
     ~exits:Term.default_exits
     ~man:[]

let () = Cmdliner.Term.(exit @@ eval cmd)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...