forked from
gazagnaire.org/irmin
Persistent store with Git semantics: lazy reads, delayed writes, content-addressing
1(** Irmin CLI configuration. *)
2
3type backend = Git | Mst | Memory
4type t = { backend : backend; store_path : string; default_branch : string }
5
6let default = { backend = Git; store_path = "."; default_branch = "main" }
7
8let pp_backend ppf = function
9 | Git -> Fmt.string ppf "git"
10 | Mst -> Fmt.string ppf "mst"
11 | Memory -> Fmt.string ppf "memory"
12
13let backend_of_string = function
14 | "git" -> Some Git
15 | "mst" | "atproto" -> Some Mst
16 | "memory" | "mem" -> Some Memory
17 | _ -> None
18
19(** Parse config file. Format: key = value, one per line. *)
20let parse_config_file path =
21 if not (Sys.file_exists path) then None
22 else
23 let ic = open_in path in
24 let rec loop acc =
25 match input_line ic with
26 | line ->
27 let line = String.trim line in
28 if line = "" || line.[0] = '#' then loop acc
29 else begin
30 match String.index_opt line '=' with
31 | None -> loop acc
32 | Some i ->
33 let key = String.trim (String.sub line 0 i) in
34 let value =
35 String.trim
36 (String.sub line (i + 1) (String.length line - i - 1))
37 in
38 loop ((key, value) :: acc)
39 end
40 | exception End_of_file ->
41 close_in ic;
42 Some acc
43 in
44 loop []
45
46let config_of_pairs pairs =
47 List.fold_left
48 (fun cfg (key, value) ->
49 match key with
50 | "backend" -> (
51 match backend_of_string value with
52 | Some b -> { cfg with backend = b }
53 | None -> cfg)
54 | "store" | "store_path" | "path" -> { cfg with store_path = value }
55 | "branch" | "default_branch" -> { cfg with default_branch = value }
56 | _ -> cfg)
57 default pairs
58
59(** Detect backend from directory structure. *)
60let detect_backend ~cwd =
61 let git_dir = Filename.concat cwd ".git" in
62 let irmin_dir = Filename.concat cwd ".irmin" in
63 if Sys.file_exists git_dir && Sys.is_directory git_dir then
64 Some { default with backend = Git; store_path = cwd }
65 else if Sys.file_exists irmin_dir && Sys.is_directory irmin_dir then
66 (* Check for config in .irmin/ *)
67 let config_path = Filename.concat irmin_dir "config" in
68 match parse_config_file config_path with
69 | Some pairs -> Some (config_of_pairs pairs)
70 | None -> Some { default with backend = Mst; store_path = irmin_dir }
71 else None
72
73(** Load configuration. Priority: explicit config > .irmin/config > auto-detect
74 > default *)
75let load ?config_file ~repo () =
76 (* First try explicit config file *)
77 match config_file with
78 | Some path -> (
79 match parse_config_file path with
80 | Some pairs -> config_of_pairs pairs
81 | None -> default)
82 | None -> (
83 (* Try .irmin/config in repo *)
84 let irmin_config = Filename.concat repo ".irmin/config" in
85 match parse_config_file irmin_config with
86 | Some pairs -> config_of_pairs pairs
87 | None -> (
88 (* Auto-detect from directory structure *)
89 match detect_backend ~cwd:repo with
90 | Some cfg -> cfg
91 | None -> { default with store_path = repo }))