ligo/src/client/client_aliases.ml

298 lines
9.1 KiB
OCaml
Raw Normal View History

2016-09-08 21:13:10 +04:00
(**************************************************************************)
(* *)
(* Copyright (c) 2014 - 2016. *)
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
(* *)
(* All rights reserved. No warranty, explicit or implicit, provided. *)
(* *)
(**************************************************************************)
(* Tezos Command line interface - Local Storage for Configuration *)
open Lwt.Infix
2016-09-08 21:13:10 +04:00
open Cli_entries
module type Entity = sig
type t
val encoding : t Data_encoding.t
val of_source :
Client_commands.context ->
string -> t tzresult Lwt.t
val to_source :
Client_commands.context ->
t -> string tzresult Lwt.t
2016-09-08 21:13:10 +04:00
val name : string
end
module type Alias = sig
type t
val load :
Client_commands.context ->
(string * t) list tzresult Lwt.t
val find :
Client_commands.context ->
string -> t tzresult Lwt.t
val find_opt :
Client_commands.context ->
string -> t option tzresult Lwt.t
val rev_find :
Client_commands.context ->
t -> string option tzresult Lwt.t
val name :
Client_commands.context ->
t -> string tzresult Lwt.t
val mem :
Client_commands.context ->
string -> bool tzresult Lwt.t
val add :
Client_commands.context ->
string -> t -> unit tzresult Lwt.t
val del :
Client_commands.context ->
string -> unit tzresult Lwt.t
val update :
Client_commands.context ->
string -> t -> unit tzresult Lwt.t
val save :
Client_commands.context ->
(string * t) list -> unit tzresult Lwt.t
val of_source :
Client_commands.context ->
string -> t tzresult Lwt.t
val to_source :
Client_commands.context ->
t -> string tzresult Lwt.t
2016-09-08 21:13:10 +04:00
val alias_param :
?name:string ->
2016-09-08 21:13:10 +04:00
?desc:string ->
('a, Client_commands.context, 'ret) Cli_entries.params ->
(string * t -> 'a, Client_commands.context, 'ret) Cli_entries.params
2016-09-08 21:13:10 +04:00
val fresh_alias_param :
?name:string ->
2016-09-08 21:13:10 +04:00
?desc:string ->
('a, Client_commands.context, 'ret) Cli_entries.params ->
(string -> 'a, Client_commands.context, 'ret) Cli_entries.params
2016-09-08 21:13:10 +04:00
val source_param :
?name:string ->
2016-09-08 21:13:10 +04:00
?desc:string ->
('a, Client_commands.context, 'ret) Cli_entries.params ->
(t -> 'a, Client_commands.context, 'ret) Cli_entries.params
2017-09-27 11:55:20 +04:00
val autocomplete:
Client_commands.context -> string list tzresult Lwt.t
2016-09-08 21:13:10 +04:00
end
module Alias = functor (Entity : Entity) -> struct
open Client_commands
2016-09-08 21:13:10 +04:00
let encoding =
let open Data_encoding in
list (obj2
(req "name" string)
(req "value" Entity.encoding))
let dirname cctxt =
cctxt.config.base_dir
let filename cctxt =
Filename.concat (dirname cctxt) (Entity.name ^ "s")
2016-09-08 21:13:10 +04:00
let load cctxt =
let filename = filename cctxt in
2017-11-08 18:46:57 +04:00
if not (Sys.file_exists filename) ||
Unix.(stat filename).st_size = 0 then
return []
else
Data_encoding_ezjsonm.read_file filename
|> generic_trace
"couldn't to read the %s alias file" Entity.name >>=? fun json ->
match Data_encoding.Json.destruct encoding json with
| exception _ -> (* TODO print_error *)
failwith "didn't understand the %s alias file" Entity.name
| list ->
return list
2016-09-08 21:13:10 +04:00
2017-09-27 11:55:20 +04:00
let autocomplete cctxt =
load cctxt >>= function
| Error _ -> return []
| Ok list -> return (List.map fst list)
let find_opt cctxt name =
load cctxt >>=? fun list ->
try return (Some (List.assoc name list))
with Not_found -> return None
2016-09-08 21:13:10 +04:00
let find cctxt name =
load cctxt >>=? fun list ->
try return (List.assoc name list)
with Not_found ->
failwith "no %s alias named %s" Entity.name name
2016-09-08 21:13:10 +04:00
let rev_find cctxt v =
load cctxt >>=? fun list ->
try return (Some (List.find (fun (_, v') -> v = v') list |> fst))
with Not_found -> return None
2016-09-08 21:13:10 +04:00
let mem cctxt name =
load cctxt >>=? fun list ->
2016-09-08 21:13:10 +04:00
try
ignore (List.assoc name list) ;
return true
2016-09-08 21:13:10 +04:00
with
| Not_found -> return false
2016-09-08 21:13:10 +04:00
let save cctxt list =
Lwt.catch
2016-09-08 21:13:10 +04:00
(fun () ->
let dirname = dirname cctxt in
Lwt_utils.create_dir dirname >>= fun () ->
let filename = filename cctxt in
2016-09-08 21:13:10 +04:00
let json = Data_encoding.Json.construct encoding list in
Data_encoding_ezjsonm.write_file filename json)
(fun exn -> Lwt.return (error_exn exn))
|> generic_trace "could not write the %s alias file." Entity.name
2016-09-08 21:13:10 +04:00
let add cctxt name value =
2016-09-08 21:13:10 +04:00
let keep = ref false in
load cctxt >>=? fun list ->
begin
if cctxt.config.force then
return ()
else
iter_s (fun (n, v) ->
if n = name && v = value then begin
keep := true ;
cctxt.message
"The %s alias %s already exists with the same value."
Entity.name n >>= fun () ->
return ()
end else if n = name && v <> value then begin
failwith
"another %s is already aliased as %s, \
use -force true to update"
Entity.name n
end else if n <> name && v = value then begin
failwith
"this %s is already aliased as %s, \
use -force true to insert duplicate"
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
return ()
2016-09-08 21:13:10 +04:00
else
save cctxt list >>=? fun () ->
cctxt.Client_commands.message
"New %s alias '%s' saved." Entity.name name >>= fun () ->
return ()
2016-09-08 21:13:10 +04:00
let del cctxt name =
load cctxt >>=? fun list ->
2016-09-08 21:13:10 +04:00
let list = List.filter (fun (n, _) -> n <> name) list in
save cctxt list
let update cctxt name value =
load cctxt >>=? fun list ->
let list =
List.map
(fun (n, v) -> (n, if n = name then value else v))
list in
save cctxt list
2016-09-08 21:13:10 +04:00
let save cctxt list =
save cctxt list >>=? fun () ->
cctxt.Client_commands.message
"Successful update of the %s alias file." Entity.name >>= fun () ->
return ()
2016-09-08 21:13:10 +04:00
include Entity
let alias_param
?(name = "name") ?(desc = "existing " ^ Entity.name ^ " alias") next =
param ~name ~desc
2017-09-27 11:55:20 +04:00
(parameter (fun cctxt s ->
find cctxt s >>=? fun v ->
return (s, v)))
next
let fresh_alias_param
?(name = "new") ?(desc = "new " ^ Entity.name ^ " alias") next =
param ~name ~desc
2017-09-27 11:55:20 +04:00
(parameter (fun cctxt s ->
begin
2017-09-27 11:55:20 +04:00
load cctxt >>=? fun list ->
begin
if cctxt.config.force then
return ()
else
iter_s
(fun (n, _v) ->
if n = s then
Entity.to_source cctxt _v >>=? fun value ->
failwith
"@[<v 2>The %s alias %s already exists.@,\
The current value is %s.@,\
Use -force true to update@]"
Entity.name n
value
2017-09-27 11:55:20 +04:00
else
return ())
list
end
end >>=? fun () ->
return s))
next
2016-12-05 16:18:12 +04:00
let source_param ?(name = "src") ?(desc = "source " ^ Entity.name) next =
let desc =
desc ^ "\n"
^ "can be an alias, file or literal (autodetected in this order)\n\
use 'file:path', 'text:literal' or 'alias:name' to force" in
param ~name ~desc
2017-09-27 11:55:20 +04:00
(parameter (fun 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 cctxt content in
begin
match Utils.split ~limit:1 ':' s with
| [ "alias" ; alias ]->
find cctxt alias
| [ "text" ; text ] ->
of_source cctxt 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 cctxt 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))
next
2016-09-08 21:13:10 +04:00
let name cctxt d =
rev_find cctxt d >>=? function
| None -> Entity.to_source cctxt d
| Some name -> return name
2016-09-08 21:13:10 +04:00
end