···11let run env app_name config =
22 let xdg = Xdge.Cmd.of_t env#fs app_name config in
33- Fmt.pr "%a@.%a@.@.%a@.%a@."
44- Fmt.(styled `Bold string) "=== Cmdliner Config ==="
55- Xdge.Cmd.pp config
66- Fmt.(styled `Bold string) "=== XDG Directories ==="
77- (Xdge.pp ~brief:false ~sources:true) xdg
33+ Fmt.pr
44+ "%a@.%a@.@.%a@.%a@."
55+ Fmt.(styled `Bold string)
66+ "=== Cmdliner Config ==="
77+ Xdge.Cmd.pp
88+ config
99+ Fmt.(styled `Bold string)
1010+ "=== XDG Directories ==="
1111+ (Xdge.pp ~brief:false ~sources:true)
1212+ xdg
1313+;;
81499-let main app_name config =
1010- Eio_main.run @@ fun env ->
1111- run env app_name config
1515+let main app_name config = Eio_main.run @@ fun env -> run env app_name config
12161317let () =
1418 Fmt.set_style_renderer Fmt.stdout `Ansi_tty;
1519 let app_name = "xdg_example" in
1620 let doc = "Example program demonstrating XDG directory selection with Cmdliner" in
1717- let man = [
1818- `S Cmdliner.Manpage.s_description;
1919- `P "This example shows how to use the Xdge library with Cmdliner \
2020- to handle XDG Base Directory Specification paths with command-line \
2121- and environment variable overrides.";
2222- `S Cmdliner.Manpage.s_environment;
2323- `P (Xdge.Cmd.env_docs app_name);
2424- ] in
2121+ let man =
2222+ [ `S Cmdliner.Manpage.s_description
2323+ ; `P
2424+ "This example shows how to use the Xdge library with Cmdliner to handle XDG Base \
2525+ Directory Specification paths with command-line and environment variable \
2626+ overrides."
2727+ ; `S Cmdliner.Manpage.s_environment
2828+ ; `P (Xdge.Cmd.env_docs app_name)
2929+ ]
3030+ in
2531 let info = Cmdliner.Cmd.info "xdg_example" ~version:"1.0" ~doc ~man in
2632 let term =
2733 let open Cmdliner.Term.Syntax in
···3036 in
3137 let cmd = Cmdliner.Cmd.v info term in
3238 exit @@ Cmdliner.Cmd.eval cmd
3939+;;
···11-type source =
11+type source =
22 | Default
33 | Env of string
44 | Cmdline
5566-type t = {
77- app_name : string;
88- config_dir : Eio.Fs.dir_ty Eio.Path.t;
99- config_dir_source : source;
1010- data_dir : Eio.Fs.dir_ty Eio.Path.t;
1111- data_dir_source : source;
1212- cache_dir : Eio.Fs.dir_ty Eio.Path.t;
1313- cache_dir_source : source;
1414- state_dir : Eio.Fs.dir_ty Eio.Path.t;
1515- state_dir_source : source;
1616- runtime_dir : Eio.Fs.dir_ty Eio.Path.t option;
1717- runtime_dir_source : source;
1818- config_dirs : Eio.Fs.dir_ty Eio.Path.t list;
1919- data_dirs : Eio.Fs.dir_ty Eio.Path.t list;
2020-}
66+type t =
77+ { app_name : string
88+ ; config_dir : Eio.Fs.dir_ty Eio.Path.t
99+ ; config_dir_source : source
1010+ ; data_dir : Eio.Fs.dir_ty Eio.Path.t
1111+ ; data_dir_source : source
1212+ ; cache_dir : Eio.Fs.dir_ty Eio.Path.t
1313+ ; cache_dir_source : source
1414+ ; state_dir : Eio.Fs.dir_ty Eio.Path.t
1515+ ; state_dir_source : source
1616+ ; runtime_dir : Eio.Fs.dir_ty Eio.Path.t option
1717+ ; runtime_dir_source : source
1818+ ; config_dirs : Eio.Fs.dir_ty Eio.Path.t list
1919+ ; data_dirs : Eio.Fs.dir_ty Eio.Path.t list
2020+ }
21212222-let ensure_dir path =
2323- Eio.Path.mkdirs ~exists_ok:true ~perm:0o755 path
2222+let ensure_dir path = Eio.Path.mkdirs ~exists_ok:true ~perm:0o755 path
24232524let get_home_dir fs =
2626- let home_str =
2525+ let home_str =
2726 match Sys.getenv_opt "HOME" with
2827 | Some home -> home
2929- | None ->
3030- match Sys.os_type with
3131- | "Win32" | "Cygwin" ->
3232- (match Sys.getenv_opt "USERPROFILE" with
3333- | Some profile -> profile
3434- | None -> failwith "Cannot determine home directory")
3535- | _ ->
3636- (try Unix.((getpwuid (getuid ())).pw_dir)
3737- with _ -> failwith "Cannot determine home directory")
2828+ | None ->
2929+ (match Sys.os_type with
3030+ | "Win32" | "Cygwin" ->
3131+ (match Sys.getenv_opt "USERPROFILE" with
3232+ | Some profile -> profile
3333+ | None -> failwith "Cannot determine home directory")
3434+ | _ ->
3535+ (try Unix.((getpwuid (getuid ())).pw_dir) with
3636+ | _ -> failwith "Cannot determine home directory"))
3837 in
3938 Eio.Path.(fs / home_str)
3939+;;
40404141-let make_env_var_name app_name suffix =
4242- String.uppercase_ascii app_name ^ "_" ^ suffix
4141+let make_env_var_name app_name suffix = String.uppercase_ascii app_name ^ "_" ^ suffix
43424443let resolve_path fs home_path base_path =
4545- if Filename.is_relative base_path then
4646- Eio.Path.(home_path / base_path)
4747- else
4848- Eio.Path.(fs / base_path)
4444+ if Filename.is_relative base_path
4545+ then Eio.Path.(home_path / base_path)
4646+ else Eio.Path.(fs / base_path)
4747+;;
49485049(* Helper to resolve system directories (config_dirs or data_dirs) *)
5150let resolve_system_dirs fs home_path app_name override_suffix xdg_var default_paths =
5251 let override_var = make_env_var_name app_name override_suffix in
5352 match Sys.getenv_opt override_var with
5454- | Some dirs when dirs <> "" ->
5555- String.split_on_char ':' dirs
5656- |> List.filter (fun s -> s <> "")
5757- |> List.map (fun path -> Eio.Path.(resolve_path fs home_path path / app_name))
5858- | Some _ | None ->
5959- match Sys.getenv_opt xdg_var with
6060- | Some dirs when dirs <> "" ->
6161- String.split_on_char ':' dirs
6262- |> List.filter (fun s -> s <> "")
6363- |> List.map (fun path -> Eio.Path.(resolve_path fs home_path path / app_name))
6464- | Some _ | None ->
6565- List.map (fun path -> Eio.Path.(fs / path / app_name)) default_paths
5353+ | Some dirs when dirs <> "" ->
5454+ String.split_on_char ':' dirs
5555+ |> List.filter (fun s -> s <> "")
5656+ |> List.map (fun path -> Eio.Path.(resolve_path fs home_path path / app_name))
5757+ | Some _ | None ->
5858+ (match Sys.getenv_opt xdg_var with
5959+ | Some dirs when dirs <> "" ->
6060+ String.split_on_char ':' dirs
6161+ |> List.filter (fun s -> s <> "")
6262+ |> List.map (fun path -> Eio.Path.(resolve_path fs home_path path / app_name))
6363+ | Some _ | None ->
6464+ List.map (fun path -> Eio.Path.(fs / path / app_name)) default_paths)
6565+;;
66666767(* Helper to resolve a user directory with override precedence *)
6868let resolve_user_dir fs home_path app_name xdg_ctx xdg_getter override_suffix =
6969 let override_var = make_env_var_name app_name override_suffix in
7070 match Sys.getenv_opt override_var with
7171- | Some dir when dir <> "" ->
7272- (resolve_path fs home_path dir, Env override_var)
7373- | Some _ | None ->
7474- (Eio.Path.(fs / xdg_getter xdg_ctx / app_name), Default)
7171+ | Some dir when dir <> "" -> resolve_path fs home_path dir, Env override_var
7272+ | Some _ | None -> Eio.Path.(fs / xdg_getter xdg_ctx / app_name), Default
7373+;;
75747675(* Helper to resolve runtime directory (special case since it can be None) *)
7776let resolve_runtime_dir fs home_path app_name xdg_ctx =
7877 let override_var = make_env_var_name app_name "RUNTIME_DIR" in
7978 match Sys.getenv_opt override_var with
8080- | Some dir when dir <> "" ->
8181- (Some (resolve_path fs home_path dir), Env override_var)
8282- | Some _ | None ->
8383- (Option.map (fun base -> Eio.Path.(fs / base / app_name)) (Xdg.runtime_dir xdg_ctx), Default)
7979+ | Some dir when dir <> "" -> Some (resolve_path fs home_path dir), Env override_var
8080+ | Some _ | None ->
8181+ ( Option.map (fun base -> Eio.Path.(fs / base / app_name)) (Xdg.runtime_dir xdg_ctx)
8282+ , Default )
8383+;;
84848585let create fs app_name =
8686 let fs = fs in
8787 let home_path = get_home_dir fs in
8888 let xdg_ctx = Xdg.create ~env:Sys.getenv_opt () in
8989-9089 (* User directories *)
9191- let (config_dir, config_dir_source) =
9292- resolve_user_dir fs home_path app_name xdg_ctx Xdg.config_dir "CONFIG_DIR" in
9393- let (data_dir, data_dir_source) =
9494- resolve_user_dir fs home_path app_name xdg_ctx Xdg.data_dir "DATA_DIR" in
9595- let (cache_dir, cache_dir_source) =
9696- resolve_user_dir fs home_path app_name xdg_ctx Xdg.cache_dir "CACHE_DIR" in
9797- let (state_dir, state_dir_source) =
9898- resolve_user_dir fs home_path app_name xdg_ctx Xdg.state_dir "STATE_DIR" in
9999-9090+ let config_dir, config_dir_source =
9191+ resolve_user_dir fs home_path app_name xdg_ctx Xdg.config_dir "CONFIG_DIR"
9292+ in
9393+ let data_dir, data_dir_source =
9494+ resolve_user_dir fs home_path app_name xdg_ctx Xdg.data_dir "DATA_DIR"
9595+ in
9696+ let cache_dir, cache_dir_source =
9797+ resolve_user_dir fs home_path app_name xdg_ctx Xdg.cache_dir "CACHE_DIR"
9898+ in
9999+ let state_dir, state_dir_source =
100100+ resolve_user_dir fs home_path app_name xdg_ctx Xdg.state_dir "STATE_DIR"
101101+ in
100102 (* Runtime directory *)
101101- let (runtime_dir, runtime_dir_source) =
102102- resolve_runtime_dir fs home_path app_name xdg_ctx in
103103-103103+ let runtime_dir, runtime_dir_source =
104104+ resolve_runtime_dir fs home_path app_name xdg_ctx
105105+ in
104106 (* System directories *)
105105- let config_dirs =
106106- resolve_system_dirs fs home_path app_name "CONFIG_DIRS" "XDG_CONFIG_DIRS"
107107- ["/etc/xdg"] in
108108- let data_dirs =
109109- resolve_system_dirs fs home_path app_name "DATA_DIRS" "XDG_DATA_DIRS"
110110- ["/usr/local/share"; "/usr/share"] in
111111-107107+ let config_dirs =
108108+ resolve_system_dirs
109109+ fs
110110+ home_path
111111+ app_name
112112+ "CONFIG_DIRS"
113113+ "XDG_CONFIG_DIRS"
114114+ [ "/etc/xdg" ]
115115+ in
116116+ let data_dirs =
117117+ resolve_system_dirs
118118+ fs
119119+ home_path
120120+ app_name
121121+ "DATA_DIRS"
122122+ "XDG_DATA_DIRS"
123123+ [ "/usr/local/share"; "/usr/share" ]
124124+ in
112125 ensure_dir config_dir;
113126 ensure_dir data_dir;
114127 ensure_dir cache_dir;
115128 ensure_dir state_dir;
116129 Option.iter ensure_dir runtime_dir;
117117-118118- { app_name; config_dir; config_dir_source; data_dir; data_dir_source;
119119- cache_dir; cache_dir_source; state_dir; state_dir_source;
120120- runtime_dir; runtime_dir_source; config_dirs; data_dirs }
130130+ { app_name
131131+ ; config_dir
132132+ ; config_dir_source
133133+ ; data_dir
134134+ ; data_dir_source
135135+ ; cache_dir
136136+ ; cache_dir_source
137137+ ; state_dir
138138+ ; state_dir_source
139139+ ; runtime_dir
140140+ ; runtime_dir_source
141141+ ; config_dirs
142142+ ; data_dirs
143143+ }
144144+;;
121145122146let app_name t = t.app_name
123147let config_dir t = t.config_dir
···132156 let path = Eio.Path.(base_dir / name) in
133157 ensure_dir path;
134158 path
159159+;;
135160136161let config_path t name = subdir t.config_dir name
137162let data_path t name = subdir t.data_dir name
138163let cache_path t name = subdir t.cache_dir name
139164let state_path t name = subdir t.state_dir name
140165141141-let pp ?(brief=false) ?(sources=false) ppf t =
166166+let pp ?(brief = false) ?(sources = false) ppf t =
142167 let pp_source ppf = function
143168 | Default -> Fmt.(styled `Faint string) ppf "default"
144169 | Env var -> Fmt.pf ppf "%a" Fmt.(styled `Yellow string) ("env(" ^ var ^ ")")
145170 | Cmdline -> Fmt.(styled `Blue string) ppf "cmdline"
146171 in
147172 let pp_path_with_source ppf path source =
148148- if sources then
149149- Fmt.pf ppf "%a %a"
150150- Fmt.(styled `Green Eio.Path.pp) path
151151- Fmt.(styled `Faint (brackets pp_source)) source
152152- else
153153- Fmt.(styled `Green Eio.Path.pp) ppf path
173173+ if sources
174174+ then
175175+ Fmt.pf
176176+ ppf
177177+ "%a %a"
178178+ Fmt.(styled `Green Eio.Path.pp)
179179+ path
180180+ Fmt.(styled `Faint (brackets pp_source))
181181+ source
182182+ else Fmt.(styled `Green Eio.Path.pp) ppf path
154183 in
155184 let pp_path_opt_with_source ppf path_opt source =
156185 match path_opt with
157157- | None ->
158158- if sources then
159159- Fmt.pf ppf "%a %a"
160160- Fmt.(styled `Red string) "<none>"
161161- Fmt.(styled `Faint (brackets pp_source)) source
162162- else
163163- Fmt.(styled `Red string) ppf "<none>"
186186+ | None ->
187187+ if sources
188188+ then
189189+ Fmt.pf
190190+ ppf
191191+ "%a %a"
192192+ Fmt.(styled `Red string)
193193+ "<none>"
194194+ Fmt.(styled `Faint (brackets pp_source))
195195+ source
196196+ else Fmt.(styled `Red string) ppf "<none>"
164197 | Some path -> pp_path_with_source ppf path source
165198 in
166199 let pp_paths ppf paths =
167200 Fmt.(list ~sep:(any ";@ ") (styled `Green Eio.Path.pp)) ppf paths
168201 in
169169- if brief then
170170- Fmt.pf ppf "%a config=%a data=%a>"
171171- Fmt.(styled `Cyan string) ("<xdg:" ^ t.app_name)
172172- (fun ppf (path, source) -> pp_path_with_source ppf path source) (t.config_dir, t.config_dir_source)
173173- (fun ppf (path, source) -> pp_path_with_source ppf path source) (t.data_dir, t.data_dir_source)
202202+ if brief
203203+ then
204204+ Fmt.pf
205205+ ppf
206206+ "%a config=%a data=%a>"
207207+ Fmt.(styled `Cyan string)
208208+ ("<xdg:" ^ t.app_name)
209209+ (fun ppf (path, source) -> pp_path_with_source ppf path source)
210210+ (t.config_dir, t.config_dir_source)
211211+ (fun ppf (path, source) -> pp_path_with_source ppf path source)
212212+ (t.data_dir, t.data_dir_source)
174213 else (
175175- Fmt.pf ppf "@[<v>%a@," Fmt.(styled `Bold string) ("XDG directories for '" ^ t.app_name ^ "':");
214214+ Fmt.pf
215215+ ppf
216216+ "@[<v>%a@,"
217217+ Fmt.(styled `Bold string)
218218+ ("XDG directories for '" ^ t.app_name ^ "':");
176219 Fmt.pf ppf "@[<v 2>%a@," Fmt.(styled `Bold string) "User directories:";
177177- Fmt.pf ppf "%a %a@," Fmt.(styled `Cyan string) "config:" (fun ppf (path, source) -> pp_path_with_source ppf path source) (t.config_dir, t.config_dir_source);
178178- Fmt.pf ppf "%a %a@," Fmt.(styled `Cyan string) "data:" (fun ppf (path, source) -> pp_path_with_source ppf path source) (t.data_dir, t.data_dir_source);
179179- Fmt.pf ppf "%a %a@," Fmt.(styled `Cyan string) "cache:" (fun ppf (path, source) -> pp_path_with_source ppf path source) (t.cache_dir, t.cache_dir_source);
180180- Fmt.pf ppf "%a %a@," Fmt.(styled `Cyan string) "state:" (fun ppf (path, source) -> pp_path_with_source ppf path source) (t.state_dir, t.state_dir_source);
181181- Fmt.pf ppf "%a %a@]@," Fmt.(styled `Cyan string) "runtime:" (fun ppf (path_opt, source) -> pp_path_opt_with_source ppf path_opt source) (t.runtime_dir, t.runtime_dir_source);
220220+ Fmt.pf
221221+ ppf
222222+ "%a %a@,"
223223+ Fmt.(styled `Cyan string)
224224+ "config:"
225225+ (fun ppf (path, source) -> pp_path_with_source ppf path source)
226226+ (t.config_dir, t.config_dir_source);
227227+ Fmt.pf
228228+ ppf
229229+ "%a %a@,"
230230+ Fmt.(styled `Cyan string)
231231+ "data:"
232232+ (fun ppf (path, source) -> pp_path_with_source ppf path source)
233233+ (t.data_dir, t.data_dir_source);
234234+ Fmt.pf
235235+ ppf
236236+ "%a %a@,"
237237+ Fmt.(styled `Cyan string)
238238+ "cache:"
239239+ (fun ppf (path, source) -> pp_path_with_source ppf path source)
240240+ (t.cache_dir, t.cache_dir_source);
241241+ Fmt.pf
242242+ ppf
243243+ "%a %a@,"
244244+ Fmt.(styled `Cyan string)
245245+ "state:"
246246+ (fun ppf (path, source) -> pp_path_with_source ppf path source)
247247+ (t.state_dir, t.state_dir_source);
248248+ Fmt.pf
249249+ ppf
250250+ "%a %a@]@,"
251251+ Fmt.(styled `Cyan string)
252252+ "runtime:"
253253+ (fun ppf (path_opt, source) -> pp_path_opt_with_source ppf path_opt source)
254254+ (t.runtime_dir, t.runtime_dir_source);
182255 Fmt.pf ppf "@[<v 2>%a@," Fmt.(styled `Bold string) "System directories:";
183183- Fmt.pf ppf "%a [@[<hov>%a@]]@," Fmt.(styled `Cyan string) "config_dirs:" pp_paths t.config_dirs;
184184- Fmt.pf ppf "%a [@[<hov>%a@]]@]@]" Fmt.(styled `Cyan string) "data_dirs:" pp_paths t.data_dirs
185185- )
256256+ Fmt.pf
257257+ ppf
258258+ "%a [@[<hov>%a@]]@,"
259259+ Fmt.(styled `Cyan string)
260260+ "config_dirs:"
261261+ pp_paths
262262+ t.config_dirs;
263263+ Fmt.pf
264264+ ppf
265265+ "%a [@[<hov>%a@]]@]@]"
266266+ Fmt.(styled `Cyan string)
267267+ "data_dirs:"
268268+ pp_paths
269269+ t.data_dirs)
270270+;;
186271187272module Cmd = struct
188188-189273 type xdg_t = t
190190-191191- type 'a with_source = {
192192- value : 'a option;
193193- source : source;
194194- }
195195-196196- type t = {
197197- config_dir : string with_source;
198198- data_dir : string with_source;
199199- cache_dir : string with_source;
200200- state_dir : string with_source;
201201- runtime_dir : string with_source;
202202- }
203203-274274+275275+ type 'a with_source =
276276+ { value : 'a option
277277+ ; source : source
278278+ }
279279+280280+ type t =
281281+ { config_dir : string with_source
282282+ ; data_dir : string with_source
283283+ ; cache_dir : string with_source
284284+ ; state_dir : string with_source
285285+ ; runtime_dir : string with_source
286286+ }
287287+204288 let term app_name =
205289 let open Cmdliner in
206290 let app_upper = String.uppercase_ascii app_name in
207207-208291 let make_dir_arg name env_suffix xdg_var default_path =
209292 let app_env = app_upper ^ "_" ^ env_suffix in
210210- let doc =
293293+ let doc =
211294 match default_path with
212212- | Some path -> Printf.sprintf
295295+ | Some path ->
296296+ Printf.sprintf
213297 "Override %s directory. Can also be set with %s or %s. Default: %s"
214214- name app_env xdg_var path
215215- | None -> Printf.sprintf
298298+ name
299299+ app_env
300300+ xdg_var
301301+ path
302302+ | None ->
303303+ Printf.sprintf
216304 "Override %s directory. Can also be set with %s or %s. No default value."
217217- name app_env xdg_var
305305+ name
306306+ app_env
307307+ xdg_var
308308+ in
309309+ let arg =
310310+ Arg.(value & opt (some string) None & info [ name ^ "-dir" ] ~docv:"DIR" ~doc)
218311 in
219219- let arg = Arg.(value & opt (some string) None &
220220- info [name ^ "-dir"] ~docv:"DIR" ~doc) in
221221- Term.(const (fun cmdline_val ->
222222- match cmdline_val with
223223- | Some v -> { value = Some v; source = Cmdline }
224224- | None ->
225225- begin match Sys.getenv_opt app_env with
226226- | Some v when v <> "" ->
227227- { value = Some v; source = Env app_env }
228228- | Some _ | None -> begin
229229- match Sys.getenv_opt xdg_var with
230230- | Some v ->
231231- { value = Some v; source = Env xdg_var }
232232- | None ->
233233- { value = None; source = Default }
234234- end
235235- end
236236- ) $ arg)
312312+ Term.(
313313+ const (fun cmdline_val ->
314314+ match cmdline_val with
315315+ | Some v -> { value = Some v; source = Cmdline }
316316+ | None ->
317317+ (match Sys.getenv_opt app_env with
318318+ | Some v when v <> "" -> { value = Some v; source = Env app_env }
319319+ | Some _ | None ->
320320+ (match Sys.getenv_opt xdg_var with
321321+ | Some v -> { value = Some v; source = Env xdg_var }
322322+ | None -> { value = None; source = Default })))
323323+ $ arg)
237324 in
238238-239325 let home_prefix = "~" in
240240- let config_dir = make_dir_arg "config" "CONFIG_DIR" "XDG_CONFIG_HOME"
241241- (Some (home_prefix ^ "/.config/" ^ app_name)) in
242242- let data_dir = make_dir_arg "data" "DATA_DIR" "XDG_DATA_HOME"
243243- (Some (home_prefix ^ "/.local/share/" ^ app_name)) in
244244- let cache_dir = make_dir_arg "cache" "CACHE_DIR" "XDG_CACHE_HOME"
245245- (Some (home_prefix ^ "/.cache/" ^ app_name)) in
246246- let state_dir = make_dir_arg "state" "STATE_DIR" "XDG_STATE_HOME"
247247- (Some (home_prefix ^ "/.local/state/" ^ app_name)) in
326326+ let config_dir =
327327+ make_dir_arg
328328+ "config"
329329+ "CONFIG_DIR"
330330+ "XDG_CONFIG_HOME"
331331+ (Some (home_prefix ^ "/.config/" ^ app_name))
332332+ in
333333+ let data_dir =
334334+ make_dir_arg
335335+ "data"
336336+ "DATA_DIR"
337337+ "XDG_DATA_HOME"
338338+ (Some (home_prefix ^ "/.local/share/" ^ app_name))
339339+ in
340340+ let cache_dir =
341341+ make_dir_arg
342342+ "cache"
343343+ "CACHE_DIR"
344344+ "XDG_CACHE_HOME"
345345+ (Some (home_prefix ^ "/.cache/" ^ app_name))
346346+ in
347347+ let state_dir =
348348+ make_dir_arg
349349+ "state"
350350+ "STATE_DIR"
351351+ "XDG_STATE_HOME"
352352+ (Some (home_prefix ^ "/.local/state/" ^ app_name))
353353+ in
248354 let runtime_dir = make_dir_arg "runtime" "RUNTIME_DIR" "XDG_RUNTIME_DIR" None in
249249-250250- Term.(const (fun config_dir data_dir cache_dir state_dir runtime_dir ->
251251- { config_dir; data_dir; cache_dir; state_dir; runtime_dir })
252252- $ config_dir $ data_dir $ cache_dir $ state_dir $ runtime_dir)
253253-355355+ Term.(
356356+ const (fun config_dir data_dir cache_dir state_dir runtime_dir ->
357357+ { config_dir; data_dir; cache_dir; state_dir; runtime_dir })
358358+ $ config_dir
359359+ $ data_dir
360360+ $ cache_dir
361361+ $ state_dir
362362+ $ runtime_dir)
363363+ ;;
364364+254365 let of_t fs app_name config =
255366 let fs = fs in
256367 let home_path = get_home_dir fs in
257368 let xdg_ctx = Xdg.create ~env:Sys.getenv_opt () in
258258-259369 (* Helper to resolve directory from config with source tracking *)
260370 let resolve_from_config config_ws xdg_getter =
261371 match config_ws.value with
262262- | Some dir -> (resolve_path fs home_path dir, config_ws.source)
372372+ | Some dir -> resolve_path fs home_path dir, config_ws.source
263373 | None ->
264264- let xdg_base = xdg_getter xdg_ctx in
265265- (Eio.Path.(fs / xdg_base / app_name), config_ws.source)
374374+ let xdg_base = xdg_getter xdg_ctx in
375375+ Eio.Path.(fs / xdg_base / app_name), config_ws.source
266376 in
267267-268377 (* User directories *)
269269- let (config_dir, config_dir_source) = resolve_from_config config.config_dir Xdg.config_dir in
270270- let (data_dir, data_dir_source) = resolve_from_config config.data_dir Xdg.data_dir in
271271- let (cache_dir, cache_dir_source) = resolve_from_config config.cache_dir Xdg.cache_dir in
272272- let (state_dir, state_dir_source) = resolve_from_config config.state_dir Xdg.state_dir in
273273-378378+ let config_dir, config_dir_source =
379379+ resolve_from_config config.config_dir Xdg.config_dir
380380+ in
381381+ let data_dir, data_dir_source = resolve_from_config config.data_dir Xdg.data_dir in
382382+ let cache_dir, cache_dir_source =
383383+ resolve_from_config config.cache_dir Xdg.cache_dir
384384+ in
385385+ let state_dir, state_dir_source =
386386+ resolve_from_config config.state_dir Xdg.state_dir
387387+ in
274388 (* Runtime directory *)
275275- let (runtime_dir, runtime_dir_source) =
389389+ let runtime_dir, runtime_dir_source =
276390 match config.runtime_dir.value with
277277- | Some dir -> (Some (resolve_path fs home_path dir), config.runtime_dir.source)
278278- | None -> (Option.map (fun base -> Eio.Path.(fs / base / app_name)) (Xdg.runtime_dir xdg_ctx), config.runtime_dir.source)
391391+ | Some dir -> Some (resolve_path fs home_path dir), config.runtime_dir.source
392392+ | None ->
393393+ ( Option.map
394394+ (fun base -> Eio.Path.(fs / base / app_name))
395395+ (Xdg.runtime_dir xdg_ctx)
396396+ , config.runtime_dir.source )
279397 in
280280-281398 (* System directories - reuse shared helper *)
282282- let config_dirs =
283283- resolve_system_dirs fs home_path app_name "CONFIG_DIRS" "XDG_CONFIG_DIRS"
284284- ["/etc/xdg"] in
285285- let data_dirs =
286286- resolve_system_dirs fs home_path app_name "DATA_DIRS" "XDG_DATA_DIRS"
287287- ["/usr/local/share"; "/usr/share"] in
288288-399399+ let config_dirs =
400400+ resolve_system_dirs
401401+ fs
402402+ home_path
403403+ app_name
404404+ "CONFIG_DIRS"
405405+ "XDG_CONFIG_DIRS"
406406+ [ "/etc/xdg" ]
407407+ in
408408+ let data_dirs =
409409+ resolve_system_dirs
410410+ fs
411411+ home_path
412412+ app_name
413413+ "DATA_DIRS"
414414+ "XDG_DATA_DIRS"
415415+ [ "/usr/local/share"; "/usr/share" ]
416416+ in
289417 ensure_dir config_dir;
290418 ensure_dir data_dir;
291419 ensure_dir cache_dir;
292420 ensure_dir state_dir;
293421 Option.iter ensure_dir runtime_dir;
294294-295295- { app_name; config_dir; config_dir_source; data_dir; data_dir_source;
296296- cache_dir; cache_dir_source; state_dir; state_dir_source;
297297- runtime_dir; runtime_dir_source; config_dirs; data_dirs }
298298-422422+ { app_name
423423+ ; config_dir
424424+ ; config_dir_source
425425+ ; data_dir
426426+ ; data_dir_source
427427+ ; cache_dir
428428+ ; cache_dir_source
429429+ ; state_dir
430430+ ; state_dir_source
431431+ ; runtime_dir
432432+ ; runtime_dir_source
433433+ ; config_dirs
434434+ ; data_dirs
435435+ }
436436+ ;;
437437+299438 let env_docs app_name =
300439 let app_upper = String.uppercase_ascii app_name in
301301- Printf.sprintf {|
440440+ Printf.sprintf
441441+ {|
302442Configuration Precedence (follows standard Unix conventions):
303443 1. Command-line flags (e.g., --config-dir) - highest priority
304444 2. Application-specific environment variable (e.g., %s_CONFIG_DIR)
···327467|}
328468 app_upper
329469 app_name
330330- app_upper app_name
331331- app_upper app_name
332332- app_upper app_name
333333- app_upper app_name
334334- app_upper app_name
335335- app_upper app_name
336336- app_name app_name app_name app_name
337337- app_name app_name app_name
338338-470470+ app_upper
471471+ app_name
472472+ app_upper
473473+ app_name
474474+ app_upper
475475+ app_name
476476+ app_upper
477477+ app_name
478478+ app_upper
479479+ app_name
480480+ app_upper
481481+ app_name
482482+ app_name
483483+ app_name
484484+ app_name
485485+ app_name
486486+ app_name
487487+ app_name
488488+ app_name
489489+ ;;
490490+339491 let pp ppf config =
340492 let pp_source ppf = function
341493 | Default -> Fmt.(styled `Faint string) ppf "default"
···345497 let pp_with_source name ppf ws =
346498 match ws.value with
347499 | None when ws.source = Default -> ()
348348- | None -> Fmt.pf ppf "@,%a %a %a"
349349- Fmt.(styled `Cyan string) (name ^ ":")
350350- Fmt.(styled `Red string) "<unset>"
351351- Fmt.(styled `Faint (brackets pp_source)) ws.source
352352- | Some value -> Fmt.pf ppf "@,%a %a %a"
353353- Fmt.(styled `Cyan string) (name ^ ":")
354354- Fmt.(styled `Green string) value
355355- Fmt.(styled `Faint (brackets pp_source)) ws.source
500500+ | None ->
501501+ Fmt.pf
502502+ ppf
503503+ "@,%a %a %a"
504504+ Fmt.(styled `Cyan string)
505505+ (name ^ ":")
506506+ Fmt.(styled `Red string)
507507+ "<unset>"
508508+ Fmt.(styled `Faint (brackets pp_source))
509509+ ws.source
510510+ | Some value ->
511511+ Fmt.pf
512512+ ppf
513513+ "@,%a %a %a"
514514+ Fmt.(styled `Cyan string)
515515+ (name ^ ":")
516516+ Fmt.(styled `Green string)
517517+ value
518518+ Fmt.(styled `Faint (brackets pp_source))
519519+ ws.source
356520 in
357357- Fmt.pf ppf "@[<v>%a%a%a%a%a%a@]"
358358- Fmt.(styled `Bold string) "XDG config:"
359359- (pp_with_source "config_dir") config.config_dir
360360- (pp_with_source "data_dir") config.data_dir
361361- (pp_with_source "cache_dir") config.cache_dir
362362- (pp_with_source "state_dir") config.state_dir
363363- (pp_with_source "runtime_dir") config.runtime_dir
521521+ Fmt.pf
522522+ ppf
523523+ "@[<v>%a%a%a%a%a%a@]"
524524+ Fmt.(styled `Bold string)
525525+ "XDG config:"
526526+ (pp_with_source "config_dir")
527527+ config.config_dir
528528+ (pp_with_source "data_dir")
529529+ config.data_dir
530530+ (pp_with_source "cache_dir")
531531+ config.cache_dir
532532+ (pp_with_source "state_dir")
533533+ config.state_dir
534534+ (pp_with_source "runtime_dir")
535535+ config.runtime_dir
536536+ ;;
364537end
+21-22
xdg-eio/lib/xdge.mli
···353353354354 This type tracks the provenance of directory paths, which is useful for
355355 debugging configuration issues and understanding which settings are in effect. *)
356356-type source =
357357- | Default (** XDG specification default value was used *)
358358- | Env of string (** Set via environment variable (includes variable name) *)
359359- | Cmdline (** Set via command-line argument *)
356356+type source =
357357+ | Default (** XDG specification default value was used *)
358358+ | Env of string (** Set via environment variable (includes variable name) *)
359359+ | Cmdline (** Set via command-line argument *)
360360361361(** {1 Cmdliner Integration} *)
362362363363module Cmd : sig
364364-365364 (** The type of the outer XDG context *)
366365 type xdg_t = t
367366 (** Cmdliner integration for XDG directory configuration.
···375374 - Environment variable detection and precedence handling
376375 - Source tracking for debugging configuration issues
377376 - Pretty printing of configuration for --help output *)
378378-377377+379378 (** A configuration value paired with information about its source.
380379381380 This type tracks both the value (if any) and where it came from,
382381 which is useful for debugging configuration issues. *)
383383- type 'a with_source = {
384384- value : 'a option; (** The configuration value, if set *)
385385- source : source; (** Where the value came from *)
386386- }
387387-382382+ type 'a with_source =
383383+ { value : 'a option (** The configuration value, if set *)
384384+ ; source : source (** Where the value came from *)
385385+ }
386386+388387 (** Complete XDG configuration gathered from command-line and environment.
389388390389 This record contains all XDG directory paths along with their sources,
391390 as determined by command-line arguments and environment variables. *)
392392- type t = {
393393- config_dir : string with_source; (** Configuration directory path and source *)
394394- data_dir : string with_source; (** Data directory path and source *)
395395- cache_dir : string with_source; (** Cache directory path and source *)
396396- state_dir : string with_source; (** State directory path and source *)
397397- runtime_dir : string with_source; (** Runtime directory path and source *)
398398- }
399399-391391+ type t =
392392+ { config_dir : string with_source (** Configuration directory path and source *)
393393+ ; data_dir : string with_source (** Data directory path and source *)
394394+ ; cache_dir : string with_source (** Cache directory path and source *)
395395+ ; state_dir : string with_source (** State directory path and source *)
396396+ ; runtime_dir : string with_source (** Runtime directory path and source *)
397397+ }
398398+400399 (** [term app_name] creates a Cmdliner term for XDG directory configuration.
401400402401 This function generates a Cmdliner term that handles all XDG directory
···426425 (* ... *)
427426 ]} *)
428427 val term : string -> t Cmdliner.Term.t
429429-428428+430429 (** [of_t fs app_name config] creates an XDG context from Cmdliner configuration.
431430432431 This function takes the configuration gathered by {!term} and creates
···446445 ...
447446 ]} *)
448447 val of_t : Eio.Fs.dir_ty Eio.Path.t -> string -> t -> xdg_t
449449-448448+450449 (** [env_docs app_name] generates documentation for environment variables.
451450452451 Returns a formatted string documenting all environment variables that
···467466 let env_section = Cmdliner.Cmd.Env.info (env_docs "myapp")
468467 ]} *)
469468 val env_docs : string -> string
470470-469469+471470 (** [pp ppf config] pretty prints a Cmdliner configuration.
472471473472 This function formats the configuration showing each directory path