2016-09-08 21:13:10 +04:00
|
|
|
(**************************************************************************)
|
|
|
|
(* *)
|
2018-02-06 00:17:03 +04:00
|
|
|
(* Copyright (c) 2014 - 2018. *)
|
2016-09-08 21:13:10 +04:00
|
|
|
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
|
|
|
(* *)
|
|
|
|
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
|
|
|
(* *)
|
|
|
|
(**************************************************************************)
|
|
|
|
|
|
|
|
(* Tezos Command line interface - Local Storage for Configuration *)
|
|
|
|
|
2017-04-05 01:35:41 +04:00
|
|
|
open Lwt.Infix
|
2018-04-03 13:39:09 +04:00
|
|
|
open Clic
|
2016-09-08 21:13:10 +04:00
|
|
|
|
|
|
|
module type Entity = sig
|
|
|
|
type t
|
|
|
|
val encoding : t Data_encoding.t
|
2017-12-05 18:09:36 +04:00
|
|
|
val of_source : string -> t tzresult Lwt.t
|
|
|
|
val to_source : t -> string tzresult Lwt.t
|
2016-09-08 21:13:10 +04:00
|
|
|
val name : string
|
|
|
|
end
|
|
|
|
|
|
|
|
module type Alias = sig
|
|
|
|
type t
|
2017-11-07 17:23:01 +04:00
|
|
|
type fresh_param
|
2016-12-03 16:05:02 +04:00
|
|
|
val load :
|
2018-02-14 18:20:03 +04:00
|
|
|
#Client_context.wallet ->
|
2017-04-05 03:02:10 +04:00
|
|
|
(string * t) list tzresult Lwt.t
|
2017-11-07 20:38:11 +04:00
|
|
|
val set :
|
2018-02-14 18:20:03 +04:00
|
|
|
#Client_context.wallet ->
|
2017-11-07 20:38:11 +04:00
|
|
|
(string * t) list ->
|
|
|
|
unit tzresult Lwt.t
|
2016-12-03 16:05:02 +04:00
|
|
|
val find :
|
2018-02-14 18:20:03 +04:00
|
|
|
#Client_context.wallet ->
|
2017-04-05 03:02:10 +04:00
|
|
|
string -> t tzresult Lwt.t
|
2016-12-03 16:05:02 +04:00
|
|
|
val find_opt :
|
2018-02-14 18:20:03 +04:00
|
|
|
#Client_context.wallet ->
|
2017-04-05 03:02:10 +04:00
|
|
|
string -> t option tzresult Lwt.t
|
2016-12-03 16:05:02 +04:00
|
|
|
val rev_find :
|
2018-02-14 18:20:03 +04:00
|
|
|
#Client_context.wallet ->
|
2017-04-05 03:02:10 +04:00
|
|
|
t -> string option tzresult Lwt.t
|
2016-12-03 16:05:02 +04:00
|
|
|
val name :
|
2018-02-14 18:20:03 +04:00
|
|
|
#Client_context.wallet ->
|
2017-04-05 03:02:10 +04:00
|
|
|
t -> string tzresult Lwt.t
|
2016-12-03 16:05:02 +04:00
|
|
|
val mem :
|
2018-02-14 18:20:03 +04:00
|
|
|
#Client_context.wallet ->
|
2017-04-05 03:02:10 +04:00
|
|
|
string -> bool tzresult Lwt.t
|
2016-12-03 16:05:02 +04:00
|
|
|
val add :
|
2017-11-07 17:23:01 +04:00
|
|
|
force:bool ->
|
2018-02-14 18:20:03 +04:00
|
|
|
#Client_context.wallet ->
|
2017-04-05 03:02:10 +04:00
|
|
|
string -> t -> unit tzresult Lwt.t
|
2016-12-03 16:05:02 +04:00
|
|
|
val del :
|
2018-02-14 18:20:03 +04:00
|
|
|
#Client_context.wallet ->
|
2017-04-05 03:02:10 +04:00
|
|
|
string -> unit tzresult Lwt.t
|
2017-03-15 04:27:34 +04:00
|
|
|
val update :
|
2018-02-14 18:20:03 +04:00
|
|
|
#Client_context.wallet ->
|
2017-04-05 03:02:10 +04:00
|
|
|
string -> t -> unit tzresult Lwt.t
|
2017-12-05 18:09:36 +04:00
|
|
|
val of_source : string -> t tzresult Lwt.t
|
|
|
|
val to_source : t -> string tzresult Lwt.t
|
2016-09-08 21:13:10 +04:00
|
|
|
val alias_param :
|
2016-11-22 17:23:40 +04:00
|
|
|
?name:string ->
|
2016-09-08 21:13:10 +04:00
|
|
|
?desc:string ->
|
2018-04-03 13:39:09 +04:00
|
|
|
('a, (#Client_context.wallet as 'b)) Clic.params ->
|
|
|
|
(string * t -> 'a, 'b) Clic.params
|
2016-09-08 21:13:10 +04:00
|
|
|
val fresh_alias_param :
|
2016-11-22 17:23:40 +04:00
|
|
|
?name:string ->
|
2016-09-08 21:13:10 +04:00
|
|
|
?desc:string ->
|
2018-04-03 13:39:09 +04:00
|
|
|
('a, (< .. > as 'obj)) Clic.params ->
|
|
|
|
(fresh_param -> 'a, 'obj) Clic.params
|
2018-01-29 13:43:07 +04:00
|
|
|
val force_switch :
|
2018-05-26 12:52:34 +04:00
|
|
|
unit -> (bool, _) arg
|
2017-11-07 17:23:01 +04:00
|
|
|
val of_fresh :
|
2018-02-14 18:20:03 +04:00
|
|
|
#Client_context.wallet ->
|
2017-11-07 17:23:01 +04:00
|
|
|
bool ->
|
|
|
|
fresh_param ->
|
|
|
|
string tzresult Lwt.t
|
2016-09-08 21:13:10 +04:00
|
|
|
val source_param :
|
2016-11-22 17:23:40 +04:00
|
|
|
?name:string ->
|
2016-09-08 21:13:10 +04:00
|
|
|
?desc:string ->
|
2018-04-03 13:39:09 +04:00
|
|
|
('a, (#Client_context.wallet as 'obj)) Clic.params ->
|
|
|
|
(t -> 'a, 'obj) Clic.params
|
2018-04-10 16:55:32 +04:00
|
|
|
val source_arg :
|
|
|
|
?long:string ->
|
|
|
|
?placeholder:string ->
|
|
|
|
?doc:string ->
|
|
|
|
unit -> (t option, (#Client_context.wallet as 'obj)) Clic.arg
|
2017-09-27 11:55:20 +04:00
|
|
|
val autocomplete:
|
2018-02-14 18:20:03 +04:00
|
|
|
#Client_context.wallet -> string list tzresult Lwt.t
|
2016-09-08 21:13:10 +04:00
|
|
|
end
|
|
|
|
|
|
|
|
module Alias = functor (Entity : Entity) -> struct
|
|
|
|
|
2018-02-14 18:20:03 +04:00
|
|
|
open Client_context
|
2017-03-15 04:17:20 +04:00
|
|
|
|
2017-11-07 20:38:11 +04:00
|
|
|
let wallet_encoding : (string * Entity.t) list Data_encoding.encoding =
|
2016-09-08 21:13:10 +04:00
|
|
|
let open Data_encoding in
|
|
|
|
list (obj2
|
|
|
|
(req "name" string)
|
|
|
|
(req "value" Entity.encoding))
|
|
|
|
|
2017-11-07 20:38:11 +04:00
|
|
|
let load (wallet : #wallet) =
|
|
|
|
wallet#load Entity.name ~default:[] wallet_encoding
|
2017-03-15 04:17:20 +04:00
|
|
|
|
2017-11-07 20:38:11 +04:00
|
|
|
let set (wallet : #wallet) entries =
|
|
|
|
wallet#write Entity.name entries wallet_encoding
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2017-11-07 20:38:11 +04:00
|
|
|
let autocomplete wallet =
|
|
|
|
load wallet >>= function
|
2017-09-27 11:55:20 +04:00
|
|
|
| Error _ -> return []
|
|
|
|
| Ok list -> return (List.map fst list)
|
|
|
|
|
2017-11-07 20:38:11 +04:00
|
|
|
let find_opt (wallet : #wallet) name =
|
|
|
|
load wallet >>=? fun list ->
|
2017-04-05 03:02:10 +04:00
|
|
|
try return (Some (List.assoc name list))
|
|
|
|
with Not_found -> return None
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2017-11-07 20:38:11 +04:00
|
|
|
let find (wallet : #wallet) name =
|
|
|
|
load wallet >>=? fun list ->
|
2017-04-05 03:02:10 +04:00
|
|
|
try return (List.assoc name list)
|
2016-12-03 16:05:02 +04:00
|
|
|
with Not_found ->
|
2017-04-05 03:02:10 +04:00
|
|
|
failwith "no %s alias named %s" Entity.name name
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2017-11-07 20:38:11 +04:00
|
|
|
let rev_find (wallet : #wallet) v =
|
|
|
|
load wallet >>=? fun list ->
|
2017-04-05 03:02:10 +04:00
|
|
|
try return (Some (List.find (fun (_, v') -> v = v') list |> fst))
|
|
|
|
with Not_found -> return None
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2017-11-07 20:38:11 +04:00
|
|
|
let mem (wallet : #wallet) name =
|
|
|
|
load wallet >>=? fun list ->
|
2016-09-08 21:13:10 +04:00
|
|
|
try
|
|
|
|
ignore (List.assoc name list) ;
|
2017-04-05 03:02:10 +04:00
|
|
|
return true
|
2016-09-08 21:13:10 +04:00
|
|
|
with
|
2017-04-05 03:02:10 +04:00
|
|
|
| Not_found -> return false
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2017-11-07 20:38:11 +04:00
|
|
|
let add ~force (wallet : #wallet) name value =
|
2016-09-08 21:13:10 +04:00
|
|
|
let keep = ref false in
|
2017-11-07 20:38:11 +04:00
|
|
|
load wallet >>=? fun list ->
|
2017-04-05 03:02:10 +04:00
|
|
|
begin
|
2017-11-07 17:23:01 +04:00
|
|
|
if force then
|
2017-04-05 03:02:10 +04:00
|
|
|
return ()
|
|
|
|
else
|
|
|
|
iter_s (fun (n, v) ->
|
|
|
|
if n = name && v = value then begin
|
|
|
|
keep := true ;
|
|
|
|
return ()
|
|
|
|
end else if n = name && v <> value then begin
|
|
|
|
failwith
|
|
|
|
"another %s is already aliased as %s, \
|
2017-11-07 20:38:11 +04:00
|
|
|
use -force to update"
|
2017-04-05 03:02:10 +04:00
|
|
|
Entity.name n
|
|
|
|
end else if n <> name && v = value then begin
|
|
|
|
failwith
|
|
|
|
"this %s is already aliased as %s, \
|
2017-11-07 20:38:11 +04:00
|
|
|
use -force to insert duplicate"
|
2017-04-05 03:02:10 +04:00
|
|
|
Entity.name n
|
|
|
|
end else begin
|
|
|
|
return ()
|
|
|
|
end)
|
|
|
|
list
|
|
|
|
end >>=? fun () ->
|
2016-09-08 21:13:10 +04:00
|
|
|
let list = List.filter (fun (n, _) -> n <> name) list in
|
|
|
|
let list = (name, value) :: list in
|
|
|
|
if !keep then
|
2017-04-05 03:02:10 +04:00
|
|
|
return ()
|
2016-09-08 21:13:10 +04:00
|
|
|
else
|
2017-11-07 20:38:11 +04:00
|
|
|
wallet#write Entity.name list wallet_encoding
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2017-11-07 20:38:11 +04:00
|
|
|
let del (wallet : #wallet) name =
|
|
|
|
load wallet >>=? fun list ->
|
2016-09-08 21:13:10 +04:00
|
|
|
let list = List.filter (fun (n, _) -> n <> name) list in
|
2017-11-07 20:38:11 +04:00
|
|
|
wallet#write Entity.name list wallet_encoding
|
2017-03-15 04:27:34 +04:00
|
|
|
|
2017-11-07 20:38:11 +04:00
|
|
|
let update (wallet : #wallet) name value =
|
|
|
|
load wallet >>=? fun list ->
|
2017-03-15 04:27:34 +04:00
|
|
|
let list =
|
|
|
|
List.map
|
|
|
|
(fun (n, v) -> (n, if n = name then value else v))
|
|
|
|
list in
|
2017-11-07 20:38:11 +04:00
|
|
|
wallet#write Entity.name list wallet_encoding
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2017-11-07 20:38:11 +04:00
|
|
|
let save wallet list =
|
|
|
|
wallet#write Entity.name wallet_encoding list
|
2016-09-08 21:13:10 +04:00
|
|
|
|
|
|
|
include Entity
|
|
|
|
|
2017-04-05 03:02:10 +04:00
|
|
|
let alias_param
|
|
|
|
?(name = "name") ?(desc = "existing " ^ Entity.name ^ " alias") next =
|
2016-11-22 17:23:40 +04:00
|
|
|
param ~name ~desc
|
2018-02-14 02:49:04 +04:00
|
|
|
(parameter
|
|
|
|
~autocomplete
|
2018-02-14 18:20:03 +04:00
|
|
|
(fun (cctxt : #Client_context.wallet) s ->
|
2018-02-14 02:49:04 +04:00
|
|
|
find cctxt s >>=? fun v ->
|
|
|
|
return (s, v)))
|
2016-11-22 17:23:40 +04:00
|
|
|
next
|
|
|
|
|
2017-11-07 17:23:01 +04:00
|
|
|
type fresh_param = Fresh of string
|
|
|
|
|
2017-11-07 20:38:11 +04:00
|
|
|
let of_fresh (wallet : #wallet) force (Fresh s) =
|
|
|
|
load wallet >>=? fun list ->
|
2017-11-07 17:23:01 +04:00
|
|
|
begin if force then
|
|
|
|
return ()
|
|
|
|
else
|
|
|
|
iter_s
|
2017-12-05 18:09:36 +04:00
|
|
|
(fun (n, v) ->
|
2017-11-07 17:23:01 +04:00
|
|
|
if n = s then
|
2017-12-05 18:09:36 +04:00
|
|
|
Entity.to_source v >>=? fun value ->
|
2017-11-07 17:23:01 +04:00
|
|
|
failwith
|
|
|
|
"@[<v 2>The %s alias %s already exists.@,\
|
|
|
|
The current value is %s.@,\
|
|
|
|
Use -force to update@]"
|
|
|
|
Entity.name n
|
|
|
|
value
|
|
|
|
else
|
|
|
|
return ())
|
|
|
|
list
|
|
|
|
end >>=? fun () ->
|
|
|
|
return s
|
|
|
|
|
2017-04-05 03:02:10 +04:00
|
|
|
let fresh_alias_param
|
|
|
|
?(name = "new") ?(desc = "new " ^ Entity.name ^ " alias") next =
|
2016-11-22 17:23:40 +04:00
|
|
|
param ~name ~desc
|
2017-11-07 20:38:11 +04:00
|
|
|
(parameter (fun (_ : < .. >) s -> return @@ Fresh s))
|
2016-11-22 17:23:40 +04:00
|
|
|
next
|
|
|
|
|
2018-04-10 16:55:32 +04:00
|
|
|
let parse_source_string cctxt s =
|
|
|
|
let read path =
|
|
|
|
Lwt.catch
|
|
|
|
(fun () ->
|
|
|
|
Lwt_io.(with_file ~mode:Input path read) >>= fun content ->
|
|
|
|
return content)
|
|
|
|
(fun exn ->
|
|
|
|
failwith
|
|
|
|
"cannot read file (%s)" (Printexc.to_string exn))
|
|
|
|
>>=? fun content ->
|
|
|
|
of_source content in
|
|
|
|
begin
|
|
|
|
match String.split ~limit:1 ':' s with
|
|
|
|
| [ "alias" ; alias ]->
|
|
|
|
find cctxt alias
|
|
|
|
| [ "text" ; text ] ->
|
|
|
|
of_source text
|
|
|
|
| [ "file" ; path ] ->
|
|
|
|
read path
|
|
|
|
| _ ->
|
|
|
|
find cctxt s >>= function
|
|
|
|
| Ok v -> return v
|
|
|
|
| Error a_errs ->
|
|
|
|
read s >>= function
|
|
|
|
| Ok v -> return v
|
|
|
|
| Error r_errs ->
|
|
|
|
of_source s >>= function
|
|
|
|
| Ok v -> return v
|
|
|
|
| Error s_errs ->
|
|
|
|
let all_errs =
|
|
|
|
List.flatten [ a_errs ; r_errs ; s_errs ] in
|
|
|
|
Lwt.return (Error all_errs)
|
|
|
|
end
|
|
|
|
|
2016-12-05 16:18:12 +04:00
|
|
|
let source_param ?(name = "src") ?(desc = "source " ^ Entity.name) next =
|
2016-11-22 17:23:40 +04:00
|
|
|
let desc =
|
2018-01-29 13:43:07 +04:00
|
|
|
Format.asprintf
|
|
|
|
"%s\n\
|
|
|
|
Can be a %s name, a file or a raw %s literal. If the \
|
|
|
|
parameter is not the name of an existing %s, the client will \
|
|
|
|
look for a file containing a %s, and if it does not exist, \
|
|
|
|
the argument will be read as a raw %s.\n\
|
|
|
|
Use 'alias:name', 'file:path' or 'text:literal' to disable \
|
|
|
|
autodetect."
|
|
|
|
desc Entity.name Entity.name Entity.name Entity.name Entity.name in
|
2016-11-22 17:23:40 +04:00
|
|
|
param ~name ~desc
|
2018-04-10 16:55:32 +04:00
|
|
|
(parameter parse_source_string)
|
2016-11-22 17:23:40 +04:00
|
|
|
next
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2018-04-10 16:55:32 +04:00
|
|
|
let source_arg
|
|
|
|
?(long = "source " ^ Entity.name)
|
|
|
|
?(placeholder = "src")
|
|
|
|
?(doc = "") () =
|
|
|
|
let doc =
|
|
|
|
Format.asprintf
|
|
|
|
"%s\n\
|
|
|
|
Can be a %s name, a file or a raw %s literal. If the \
|
|
|
|
parameter is not the name of an existing %s, the client will \
|
|
|
|
look for a file containing a %s, and if it does not exist, \
|
|
|
|
the argument will be read as a raw %s.\n\
|
|
|
|
Use 'alias:name', 'file:path' or 'text:literal' to disable \
|
|
|
|
autodetect."
|
|
|
|
doc Entity.name Entity.name Entity.name Entity.name Entity.name in
|
|
|
|
arg
|
|
|
|
~long
|
|
|
|
~placeholder
|
|
|
|
~doc
|
|
|
|
(parameter parse_source_string)
|
|
|
|
|
2018-02-11 22:17:39 +04:00
|
|
|
let force_switch () =
|
2018-04-03 13:39:09 +04:00
|
|
|
Clic.switch
|
2018-02-14 18:54:52 +04:00
|
|
|
~long:"force" ~short:'f'
|
2018-01-29 13:43:07 +04:00
|
|
|
~doc:("overwrite existing " ^ Entity.name) ()
|
|
|
|
|
2017-11-07 20:38:11 +04:00
|
|
|
let name (wallet : #wallet) d =
|
|
|
|
rev_find wallet d >>=? function
|
2017-12-05 18:09:36 +04:00
|
|
|
| None -> Entity.to_source d
|
2017-11-13 19:34:00 +04:00
|
|
|
| Some name -> return name
|
2016-09-08 21:13:10 +04:00
|
|
|
|
|
|
|
end
|