1 | module Validation = struct
|
---|
2 | let empty = []
|
---|
3 |
|
---|
4 | let (&>) report = function None -> report | Some msg -> msg :: report
|
---|
5 | let (&&>) report = function [] -> report | msgs -> msgs @ report
|
---|
6 |
|
---|
7 | let check ok msg = if ok then None else Some msg
|
---|
8 |
|
---|
9 | let file_exists ?(msg=(fun s -> (s ^ " is not a file"))) ?(parent_dir=Fpath.v ".") file =
|
---|
10 | let str = Fpath.(to_string (parent_dir // file)) in
|
---|
11 | check (Sys.file_exists str) (msg str)
|
---|
12 |
|
---|
13 | let is_directory ?(msg=(fun s -> (s ^ " is not a directory"))) dir =
|
---|
14 | let str = Fpath.to_string dir in
|
---|
15 | check (Sys.file_exists str && Sys.is_directory str) (msg str)
|
---|
16 |
|
---|
17 | let files_exist ?(msg=(fun s -> (s ^ " is not a file"))) ?(parent_dir=Fpath.v ".") files =
|
---|
18 | let f report file = report &> file_exists ~msg ~parent_dir file in
|
---|
19 | List.fold_left f empty files
|
---|
20 |
|
---|
21 | let terminate_when_invalid ?(print_error=true) =
|
---|
22 | let error i msg = prerr_endline ("Error " ^ string_of_int i ^ ": " ^ msg) in
|
---|
23 | function
|
---|
24 | | [] -> ()
|
---|
25 | | msgs -> if print_error then List.iteri error (List.rev msgs); exit 1
|
---|
26 | end
|
---|
27 |
|
---|
28 | module Path = struct
|
---|
29 | let of_string str =
|
---|
30 | if Sys.file_exists str then
|
---|
31 | match Fpath.v str with
|
---|
32 | | path -> Ok path
|
---|
33 | | exception (Invalid_argument msg) -> Error ("Invalid path " ^ msg)
|
---|
34 | else Error (str ^ " not found")
|
---|
35 |
|
---|
36 | let path_exists x = Fpath.to_string x |> Sys.file_exists
|
---|
37 |
|
---|
38 | let conventional_paths =
|
---|
39 | let paths =
|
---|
40 | try [ ".logarion"; Sys.getenv "HOME" ^ "/.config/logarion"; "/etc/logarion" ]
|
---|
41 | with Not_found -> [ ".logarion"; "/etc/logarion" ]
|
---|
42 | in
|
---|
43 | List.map Fpath.v paths
|
---|
44 |
|
---|
45 | let with_file ?(conventional_paths=conventional_paths) config_file =
|
---|
46 | let (//) = Fpath.(//) in
|
---|
47 | let basepath = Fpath.v config_file in
|
---|
48 | let existing dir = path_exists (dir // basepath) in
|
---|
49 | try Ok (List.find existing conventional_paths // basepath)
|
---|
50 | with Not_found -> Error (config_file ^ " not found in: " ^ String.concat ", " (List.map Fpath.to_string conventional_paths))
|
---|
51 | end
|
---|
52 |
|
---|
53 | let with_default default = function Some x -> x | None -> default
|
---|
54 |
|
---|
55 | let with_default_paths default =
|
---|
56 | function Some ss -> List.map Fpath.v ss | None -> default
|
---|
57 |
|
---|
58 | let mandatory = function Some x -> x | None -> failwith "undefined mandatory setting"
|
---|
59 |
|
---|
60 | let (&>) a b = match a with Ok x -> b x | Error e -> Error e
|
---|
61 |
|
---|
62 | module type Store = sig
|
---|
63 | type t
|
---|
64 | val from_path : Fpath.t -> (t, string) result
|
---|
65 | end
|
---|
66 |
|
---|
67 | module Make (S : Store) = struct
|
---|
68 | include S
|
---|
69 |
|
---|
70 | let of_path path = S.from_path path
|
---|
71 |
|
---|
72 | let (&>) = (&>)
|
---|
73 |
|
---|
74 | let to_record converter = function
|
---|
75 | | Ok store -> converter store
|
---|
76 | | Error s -> Error s
|
---|
77 |
|
---|
78 | let to_record_or_exit ?(print_error=true) ?(validator=(fun _cfg -> [])) converter store_result =
|
---|
79 | match to_record converter store_result with
|
---|
80 | | Ok cfg -> Validation.terminate_when_invalid (validator cfg); cfg
|
---|
81 | | Error s -> if print_error then prerr_endline s; exit 1
|
---|
82 | end
|
---|