Client: move Unix out of Cli_entries and a bit of output cleanup

This commit is contained in:
Benjamin Canou 2018-02-13 17:56:47 +01:00
parent 2c2f0a1818
commit 42566e9dcd
18 changed files with 909 additions and 886 deletions

View File

@ -8,7 +8,7 @@ _tezos-client_complete()
# Tezos script
script=${COMP_WORDS[0]}
reply=$($script bash_autocomplete "$prev_word" "$cur_word" ${COMP_WORDS[@]})
reply=$($script bash_autocomplete "$prev_word" "$cur_word" ${COMP_WORDS[@]} 2>/dev/null)
COMPREPLY=($(compgen -W "$reply" -- $cur_word))

View File

@ -23,7 +23,7 @@ let get_commands_for_version ctxt block protocol =
Format.eprintf
"@[<v 2>Warning:@,\
The protocol provided via `-protocol` (%a)@,\
is not the one retrieved from the node (%a).@."
is not the one retrieved from the node (%a).@]@\n@."
Protocol_hash.pp_short given_version
Protocol_hash.pp_short version ;
return (Some version, Client_commands.commands_for_version given_version)
@ -33,7 +33,8 @@ let get_commands_for_version ctxt block protocol =
match protocol with
| None -> begin
Format.eprintf
"@[<v 2>Ignored error:@,Failed to acquire the protocol version from the node@,%a@."
"@[<v 2>@{<warning>@{<title>Warning@}@}@,\
Failed to acquire the protocol version from the node@,%a@]@\n@."
(Format.pp_print_list pp) errs ;
return (None, [])
end
@ -43,10 +44,25 @@ let get_commands_for_version ctxt block protocol =
(* Main (lwt) entry *)
let main ?only_commands () =
let executable_name = Filename.basename Sys.executable_name in
let global_options = Client_config.global_options () in
let original_args, autocomplete =
(* for shell aliases *)
let rec move_autocomplete_token_upfront acc = function
| "bash_autocomplete" :: prev_arg :: cur_arg :: script :: args ->
let args = List.rev acc @ args in
args, Some (prev_arg, cur_arg, script)
| x :: rest -> move_autocomplete_token_upfront (x :: acc) rest
| [] -> List.rev acc, None in
match Array.to_list Sys.argv with
| _ :: args -> move_autocomplete_token_upfront [] args
| [] -> [], None in
Random.self_init () ;
Lwt.catch begin fun () ->
let original_args = List.tl (Array.to_list Sys.argv) in
begin
ignore Cli_entries.(setup_formatter Format.std_formatter
(if Unix.isatty Unix.stdout then Ansi else Plain) Short) ;
ignore Cli_entries.(setup_formatter Format.err_formatter
(if Unix.isatty Unix.stderr then Ansi else Plain) Short) ;
Lwt.catch begin fun () -> begin
Client_config.parse_config_args
(cctxt ~base_dir:Client_commands.default_base_dir
~block:Client_commands.default_block
@ -75,6 +91,13 @@ let main ?only_commands () =
| Some commands ->
return (config_commands @ commands)
end >>=? fun commands ->
let commands =
Cli_entries.add_manual
~executable_name
~global_options
(if Unix.isatty Unix.stdout then Cli_entries.Ansi else Cli_entries.Plain)
Format.std_formatter
commands in
let rpc_config =
if parsed_args.print_timings then
{ rpc_config with
@ -85,35 +108,45 @@ let main ?only_commands () =
in
let client_config =
cctxt ~block:parsed_args.block ~base_dir:parsed_config_file.base_dir rpc_config in
(Cli_entries.dispatch
~global_options:(Client_config.global_options ())
commands
client_config
remaining) end >>=
Cli_entries.handle_cli_errors
~stdout:Format.std_formatter
~stderr:Format.err_formatter
~global_options:(Client_config.global_options ())
>>= function
| Ok i ->
Lwt.return i
begin match autocomplete with
| Some (prev_arg, cur_arg, script) ->
Cli_entries.autocompletion
~script ~cur_arg ~prev_arg ~args:original_args ~global_options
commands client_config >>=? fun completions ->
List.iter print_endline completions ;
return ()
| None ->
Cli_entries.dispatch commands client_config remaining
end
end >>= function
| Ok () ->
Lwt.return 0
| Error [ Cli_entries.Help command ] ->
Cli_entries.usage
Format.std_formatter
~executable_name
~global_options
(match command with None -> [] | Some c -> [ c ]) ;
Lwt.return 0
| Error errs ->
Format.eprintf "@[<v 2>Fatal error:@,%a@."
(Format.pp_print_list Error_monad.pp) errs ;
Cli_entries.pp_cli_errors
Format.err_formatter
~executable_name
~global_options
~default:Error_monad.pp
errs ;
Lwt.return 1
end begin function
| Arg.Help help ->
Format.printf "%s%!" help ;
Lwt.return 0
| Client_commands.Version_not_found ->
Format.eprintf "Unknown protocol version.@." ;
| Client_commands.Version_not_found ->
Format.eprintf "@{<error>@{<title>Fatal error@}@} unknown protocol version." ;
Lwt.return 1
| Failure message ->
Format.eprintf
"Fatal error: %s@." message ;
Format.eprintf "@{<error>@{<title>Fatal error@}@} %s." message ;
Lwt.return 1
| exn ->
Format.printf "Fatal internal error: %s@."
(Printexc.to_string exn) ;
Format.printf "@{<error>@{<title>Fatal error@}@} %s." (Printexc.to_string exn) ;
Lwt.return 1
end
end >>= fun retcode ->
Format.fprintf Format.std_formatter "@." ;
Format.fprintf Format.err_formatter "@." ;
Lwt.return retcode

File diff suppressed because it is too large Load Diff

View File

@ -81,7 +81,7 @@ val switch : doc:string -> parameter:string ->
(** {2 Groups of Optional Arguments} *)
(** Defines a group of options, either the global options or the
command options. *)
command options. *)
(** The type of a series of labeled arguments to a command *)
type ('a, 'ctx) options
@ -163,56 +163,56 @@ val args10 : ('a, 'ctx) arg -> ('b, 'ctx) arg -> ('c, 'ctx) arg -> ('d, 'ctx) ar
(** {2 Parameter based command lines} *)
(** Type of parameters for a command *)
type ('a, 'ctx, 'ret) params
type ('a, 'ctx) params
(** A piece of data inside a command line *)
val param:
name: string ->
desc: string ->
('a, 'ctx) parameter ->
('b, 'ctx, 'ret) params ->
('a -> 'b, 'ctx, 'ret) params
('b, 'ctx) params ->
('a -> 'b, 'ctx) params
(** A word in a command line.
Should be descriptive. *)
val prefix:
string ->
('a, 'ctx, 'ret) params ->
('a, 'ctx, 'ret) params
('a, 'ctx) params ->
('a, 'ctx) params
(** Multiple words given in sequence for a command line *)
val prefixes:
string list ->
('a, 'ctx, 'ret) params ->
('a, 'ctx, 'ret) params
('a, 'ctx) params ->
('a, 'ctx) params
(** A fixed series of words that trigger a command. *)
val fixed:
string list ->
('ctx -> 'ret tzresult Lwt.t, 'ctx, 'ret) params
('ctx -> unit tzresult Lwt.t, 'ctx) params
(** End the description of the command line *)
val stop:
('ctx -> 'ret tzresult Lwt.t, 'ctx, 'ret) params
('ctx -> unit tzresult Lwt.t, 'ctx) params
(** Take a sequence of parameters instead of only a single one.
Must be the last thing in the command line. *)
val seq_of_param:
(('ctx -> 'ret tzresult Lwt.t, 'ctx, 'ret) params ->
('a -> 'ctx -> 'ret tzresult Lwt.t, 'ctx, 'ret) params) ->
('a list -> 'ctx -> 'ret tzresult Lwt.t, 'ctx, 'ret) params
(('ctx -> unit tzresult Lwt.t, 'ctx) params ->
('a -> 'ctx -> unit tzresult Lwt.t, 'ctx) params) ->
('a list -> 'ctx -> unit tzresult Lwt.t, 'ctx) params
(** Parameter that expects a string *)
val string:
name: string ->
desc: string ->
('a, 'ctx, 'ret) params ->
(string -> 'a, 'ctx, 'ret) params
('a, 'ctx) params ->
(string -> 'a, 'ctx) params
(** {2 Commands } *)
(** Command, including a parameter specification, optional arguments, and handlers *)
type ('ctx, 'ret) command
type 'ctx command
(** Type of a group of commands.
Groups have their documentation printed together
@ -222,49 +222,30 @@ type group =
title : string }
(** A complete command, with documentation, a specification of its
options, parameters, and handler function. *)
options, parameters, and handler function. *)
val command:
?group: group ->
desc: string ->
('b, 'ctx) options ->
('a, 'ctx, 'ret) params ->
('a, 'ctx) params ->
('b -> 'a) ->
('ctx, 'ret) command
'ctx command
(** {2 Parsing and error reporting} *)
(** Print readable descriptions for CLI parsing errors.
This function must be used for help printing to work. *)
val handle_cli_errors:
stdout: Format.formatter ->
stderr: Format.formatter ->
global_options:(_, _) options ->
'a tzresult -> int tzresult Lwt.t
(** Find and call the applicable command on the series of arguments.
@raises [Failure] if the command list would be ambiguous. *)
val dispatch:
?global_options:('a, 'ctx) options ->
('ctx, 'ret) command list ->
'ctx ->
string list ->
'ret tzresult Lwt.t
(** Parse the global options, and return their value, with the rest of
the command to be parsed. *)
val parse_initial_options :
('a, 'ctx) options ->
'ctx ->
string list ->
('a * string list) tzresult Lwt.t
val map_command: ('a -> 'b) -> ('b, 'c) command -> ('a, 'c) command
(** Combinator to use a command in an adaptated context. *)
val map_command: ('a -> 'b) -> 'b command -> 'a command
(** {2 Output formatting} *)
(** Used to restore the formatter state after [setup_formatter]. *)
type formatter_state
(** Supported output formats.
Currently: black and white, colors using ANSI escapes, and HTML.*)
type format = Plain | Ansi | Html
(** Verbosity level, from terse to verbose. *)
type verbosity = Terse | Short | Details | Full
(** Updates the formatter's functions to interprete some semantic tags
used in manual production. Returns the previous state of the
formatter to restore it afterwards if needed.
@ -275,28 +256,86 @@ type formatter_state
* [<title>]: a section title (just below a [<document])
* [<list>]: a list section (just below a [<document])
Structure tags used internally for generating the manual:
Structure tags used internally for generating the manual:
* [<command>]: wraps the full documentation bloc for a command
* [<commandline>]: wraps the command line in a [<command>]
* [<commanddoc>]: wraps everything but the command line in a [<command>]
Cosmetic tags for hilighting text:
Cosmetic tags for hilighting text:
* [<opt>]: optional arguments * [<arg>]: positional arguments
* [<kwd>]: positional keywords * [<hilight>]: search results
Verbosity levels, in order, and how they are used in the manual:
Verbosity levels, in order, and how they are used in the manual:
* [<terse>]: always displayed (titles commands lines)
* [<args>]: displayed if [verbosity >= `Args] (lists of arguments)
* [<short>]: displayed if [verbosity >= `Short] (single line descriptions)
* [<full>]: only displayed if [verbosity = `Full] (long descriptions) *)
* [<terse>]: titles, commands lines
* [<short>]: lists of arguments
* [<details>]: single line descriptions
* [<full>]: with long descriptions
Wrapping a piece of text with a debug level means that the
contents are only printed if the verbosity is equal to or
above that level. Use prefix [=] for an exact match, or [-]
for the inverse interpretation. *)
val setup_formatter :
Format.formatter ->
format: [< `Ansi | `Html | `Plain ] ->
verbosity: [> `Terse | `Short | `Args | `Full ] ->
format ->
verbosity ->
formatter_state
(** Restore the formatter state after [setup_formatter]. *)
val restore_formatter : Format.formatter -> formatter_state -> unit
(** {2 Parsing and error reporting} *)
(** Help error (not really an error), thrown by {!dispatch} and {!parse_initial_options}. *)
type error += Help : _ command option -> error
(** Find and call the applicable command on the series of arguments.
@raises [Failure] if the command list would be ambiguous. *)
val dispatch: 'ctx command list -> 'ctx -> string list -> unit tzresult Lwt.t
(** Parse the global options, and return their value, with the rest of
the command to be parsed. *)
val parse_global_options : ('a, 'ctx) options -> 'ctx -> string list -> ('a * string list) tzresult Lwt.t
(** Pretty printfs the error messages to the given formatter.
[executable_name] and [global_options] are for help screens.
[default] is used to print non-cli errors. *)
val pp_cli_errors :
Format.formatter ->
executable_name: string ->
global_options: (_, _) options ->
default: (Format.formatter -> error -> unit) ->
error list ->
unit
(** Acts as {!dispatch}, but stops if the given command up to
[prev_arg] is a valid prefix command, returning the list of valid
next words, filtered with [cur_arg]. *)
val autocompletion :
script:string -> cur_arg:string -> prev_arg:string -> args:string list ->
global_options:('a, 'ctx) options -> 'ctx command list -> 'ctx ->
string list Error_monad.tzresult Lwt.t
(** Displays a help page for the given commands. *)
val usage :
Format.formatter ->
executable_name:string ->
global_options:(_, _) options ->
_ command list ->
unit
(** {2 Manual} *)
(** Add manual commands to a list of commands.
For this to work, the command list must be complete.
Commands added later will not appear in the manual. *)
val add_manual :
executable_name: string ->
global_options: ('a, 'ctx) options ->
format ->
Format.formatter ->
'ctx command list ->
'ctx command list

View File

@ -10,8 +10,8 @@ module Public_key : sig
val param:
?name:string ->
?desc:string ->
('a, 'b, 'c) Cli_entries.params ->
(t -> 'a, 'b, 'c) Cli_entries.params
('a, 'b) Cli_entries.params ->
(t -> 'a, 'b) Cli_entries.params
val of_b58check: string -> t tzresult
end
@ -21,8 +21,8 @@ module Secret_key : sig
val param:
?name:string ->
?desc:string ->
('a, 'b, 'c) Cli_entries.params ->
(t -> 'a, 'b, 'c) Cli_entries.params
('a, 'b) Cli_entries.params ->
(t -> 'a, 'b) Cli_entries.params
val of_b58check: string -> t tzresult
end
@ -32,8 +32,8 @@ module Signature : sig
val param:
?name:string ->
?desc:string ->
('a, 'b, 'c) Cli_entries.params ->
(t -> 'a, 'b, 'c) Cli_entries.params
('a, 'b) Cli_entries.params ->
(t -> 'a, 'b) Cli_entries.params
val of_b58check: string -> t tzresult
end

View File

@ -78,8 +78,8 @@ module type INTERNAL_HASH = sig
val param:
?name:string ->
?desc:string ->
('a, 'arg, 'ret) Cli_entries.params ->
(t -> 'a, 'arg, 'ret) Cli_entries.params
('a, 'arg) Cli_entries.params ->
(t -> 'a, 'arg) Cli_entries.params
module Set : sig
include Set.S with type elt = t
@ -128,8 +128,8 @@ module type INTERNAL_MERKLE_TREE = sig
val param:
?name:string ->
?desc:string ->
('a, 'arg, 'ret) Cli_entries.params ->
(t -> 'a, 'arg, 'ret) Cli_entries.params
('a, 'arg) Cli_entries.params ->
(t -> 'a, 'arg) Cli_entries.params
module Set : sig
include Set.S with type elt = t

View File

@ -7,4 +7,4 @@
(* *)
(**************************************************************************)
val commands : unit -> (#Client_commands.full_context, unit) Cli_entries.command list
val commands : unit -> #Client_commands.full_context Cli_entries.command list

View File

@ -60,13 +60,13 @@ module type Alias = sig
val alias_param :
?name:string ->
?desc:string ->
('a, (#Client_commands.wallet as 'b), 'ret) Cli_entries.params ->
(string * t -> 'a, 'b, 'ret) Cli_entries.params
('a, (#Client_commands.wallet as 'b)) Cli_entries.params ->
(string * t -> 'a, 'b) Cli_entries.params
val fresh_alias_param :
?name:string ->
?desc:string ->
('a, (< .. > as 'obj), 'ret) Cli_entries.params ->
(fresh_param -> 'a, 'obj, 'ret) Cli_entries.params
('a, (< .. > as 'obj)) Cli_entries.params ->
(fresh_param -> 'a, 'obj) Cli_entries.params
val force_switch :
unit -> (bool, #Client_commands.full_context) arg
val of_fresh :
@ -77,8 +77,8 @@ module type Alias = sig
val source_param :
?name:string ->
?desc:string ->
('a, (#Client_commands.wallet as 'obj), 'ret) Cli_entries.params ->
(t -> 'a, 'obj, 'ret) Cli_entries.params
('a, (#Client_commands.wallet as 'obj)) Cli_entries.params ->
(t -> 'a, 'obj) Cli_entries.params
val autocomplete:
#Client_commands.wallet -> string list tzresult Lwt.t
end

View File

@ -56,13 +56,13 @@ module type Alias = sig
val alias_param :
?name:string ->
?desc:string ->
('a, (#Client_commands.wallet as 'b), 'ret) Cli_entries.params ->
(string * t -> 'a, 'b, 'ret) Cli_entries.params
('a, (#Client_commands.wallet as 'b)) Cli_entries.params ->
(string * t -> 'a, 'b) Cli_entries.params
val fresh_alias_param :
?name:string ->
?desc:string ->
('a, (< .. > as 'obj), 'ret) Cli_entries.params ->
(fresh_param -> 'a, 'obj, 'ret) Cli_entries.params
('a, (< .. > as 'obj)) Cli_entries.params ->
(fresh_param -> 'a, 'obj) Cli_entries.params
val force_switch :
unit -> (bool, #Client_commands.full_context) Cli_entries.arg
val of_fresh :
@ -73,8 +73,8 @@ module type Alias = sig
val source_param :
?name:string ->
?desc:string ->
('a, (#Client_commands.wallet as 'obj), 'ret) Cli_entries.params ->
(t -> 'a, 'obj, 'ret) Cli_entries.params
('a, (#Client_commands.wallet as 'obj)) Cli_entries.params ->
(t -> 'a, 'obj) Cli_entries.params
val autocomplete:
#Client_commands.wallet -> string list tzresult Lwt.t
end

View File

@ -121,7 +121,7 @@ class file_wallet dir : wallet = object (self)
|> generic_trace "could not write the %s alias file." alias_name
end
type command = (full_context, unit) Cli_entries.command
type command = full_context Cli_entries.command
(* Default config *)

View File

@ -70,7 +70,7 @@ val ignore_context : full_context
(** [ignore_context] is a context whose logging callbacks do nothing,
and whose [error] function calls [Lwt.fail_with]. *)
type command = (full_context, unit) Cli_entries.command
type command = full_context Cli_entries.command
exception Version_not_found

View File

@ -20,7 +20,7 @@ let () =
~description: "Block argument could not be parsed"
~pp:
(fun ppf s ->
Format.fprintf ppf "Value provided for -block flag (%s) could not be parsed" s)
Format.fprintf ppf "Value %s is not a value block reference." s)
Data_encoding.(obj1 (req "value" string))
(function Invalid_block_argument s -> Some s | _ -> None)
(fun s -> Invalid_block_argument s) ;
@ -31,7 +31,7 @@ let () =
~description: "Protocol argument could not be parsed"
~pp:
(fun ppf s ->
Format.fprintf ppf "Value provided for -protocol flag (%s) does not correspond to any known protocol" s)
Format.fprintf ppf "Value %s does not correspond to any known protocol." s)
Data_encoding.(obj1 (req "value" string))
(function Invalid_protocol_argument s -> Some s | _ -> None)
(fun s -> Invalid_protocol_argument s) ;
@ -42,7 +42,7 @@ let () =
~description: "Port argument could not be parsed"
~pp:
(fun ppf s ->
Format.fprintf ppf "Value provided for -port flag (%s) could not be parsed" s)
Format.fprintf ppf "Value %s is not a valid TCP port." s)
Data_encoding.(obj1 (req "value" string))
(function Invalid_port_arg s -> Some s | _ -> None)
(fun s -> Invalid_port_arg s)
@ -282,7 +282,7 @@ let global_options () =
(tls_switch ())
let parse_config_args (ctx : #Client_commands.full_context) argv =
parse_initial_options
parse_global_options
(global_options ())
ctx
argv >>=?

View File

@ -8,4 +8,4 @@
(**************************************************************************)
val commands : unit -> (#Client_commands.full_context, unit) Cli_entries.command list
val commands : unit -> #Client_commands.full_context Cli_entries.command list

View File

@ -28,8 +28,8 @@ module Tags (Entity : Entity) : sig
val tag_param:
?name:string ->
?desc:string ->
('a, 'ctx, 'ret) Cli_entries.params ->
(Tag.t -> 'a, 'ctx, 'ret) Cli_entries.params
('a, 'ctx) Cli_entries.params ->
(Tag.t -> 'a, 'ctx) Cli_entries.params
val rev_find_by_tag:
#Client_commands.full_context ->

View File

@ -7,4 +7,4 @@
(* *)
(**************************************************************************)
val commands: unit -> (Proto_alpha.full_context, unit) Cli_entries.command list
val commands: unit -> Proto_alpha.full_context Cli_entries.command list

View File

@ -35,8 +35,8 @@ val tez_arg :
val tez_param :
name:string ->
desc:string ->
('a, full_context, 'ret) Cli_entries.params ->
(Tez.t -> 'a, full_context, 'ret) Cli_entries.params
('a, full_context) Cli_entries.params ->
(Tez.t -> 'a, full_context) Cli_entries.params
module Daemon : sig
val baking_switch: (bool, Proto_alpha.full_context) Cli_entries.arg

View File

@ -21,13 +21,13 @@ module ContractAlias : sig
val alias_param:
?name:string ->
?desc:string ->
('a, (#Client_commands.wallet as 'wallet), 'ret) params ->
(Lwt_io.file_name * Contract.t -> 'a, 'wallet, 'ret) params
('a, (#Client_commands.wallet as 'wallet)) params ->
(Lwt_io.file_name * Contract.t -> 'a, 'wallet) params
val destination_param:
?name:string ->
?desc:string ->
('a, (#Client_commands.wallet as 'wallet), 'ret) params ->
(Lwt_io.file_name * Contract.t -> 'a, 'wallet, 'ret) params
('a, (#Client_commands.wallet as 'wallet)) params ->
(Lwt_io.file_name * Contract.t -> 'a, 'wallet) params
val rev_find:
#Client_commands.wallet ->
Contract.t -> string option tzresult Lwt.t

View File

@ -7,4 +7,4 @@
(* *)
(**************************************************************************)
val commands: unit -> (Proto_alpha.full_context, unit) Cli_entries.command list
val commands: unit -> Proto_alpha.full_context Cli_entries.command list