XDG library path support for OCaml via Eio capabilities
linux
macos
ocaml
xdg
1(** XDG Base Directory Specification support with Eio capabilities
2
3 This library provides an OCaml implementation of the XDG Base Directory
4 Specification with Eio filesystem integration. The XDG specification defines
5 standard locations for user-specific and system-wide application files,
6 helping to keep user home directories clean and organized.
7
8 The specification is available at:
9 {{:https://specifications.freedesktop.org/basedir-spec/latest/} XDG Base
10 Directory Specification}
11
12 {b Key Concepts:}
13
14 The XDG specification defines several types of directories:
15 - {b User directories}: Store user-specific files (config, data, cache,
16 state, runtime)
17 - {b System directories}: Store system-wide files shared across users
18 - {b Precedence}: User directories take precedence over system directories
19 - {b Application isolation}: Each application gets its own subdirectory
20
21 {b Environment Variable Precedence:}
22
23 This library follows a three-level precedence system:
24 + Application-specific variables (e.g., [MYAPP_CONFIG_DIR]) - highest
25 priority
26 + XDG standard variables (e.g., [XDG_CONFIG_HOME])
27 + Default paths (e.g., [$HOME/.config]) - lowest priority
28
29 This allows fine-grained control over directory locations without affecting
30 other XDG-compliant applications.
31
32 {b Directory Creation:}
33
34 All directories are automatically created with appropriate permissions
35 (0o755) when accessed, except for runtime directories which require stricter
36 permissions as per the specification.
37
38 @see <https://specifications.freedesktop.org/basedir-spec/latest/>
39 XDG Base Directory Specification *)
40
41type t
42(** The main XDG context type containing all directory paths for an application.
43
44 A value of type [t] represents the complete XDG directory structure for a
45 specific application, including both user-specific and system-wide
46 directories. All paths are resolved at creation time and are absolute paths
47 within the Eio filesystem. *)
48
49(** {1 Exceptions} *)
50
51exception Invalid_xdg_path of string
52(** Exception raised when XDG environment variables contain invalid paths.
53
54 The XDG specification requires all paths in environment variables to be
55 absolute. This exception is raised when a relative path is found. *)
56
57(** {1 Construction} *)
58
59val create : Eio.Fs.dir_ty Eio.Path.t -> string -> t
60(** [create fs app_name] creates an XDG context for the given application.
61
62 This function initializes the complete XDG directory structure for your
63 application, resolving all paths according to the environment variables and
64 creating directories as needed.
65
66 @param fs The Eio filesystem providing filesystem access
67 @param app_name The name of your application (used as subdirectory name)
68
69 {b Path Resolution:}
70
71 For each directory type, the following precedence is used:
72 + Application-specific environment variable (e.g., [MYAPP_CONFIG_DIR])
73 + XDG standard environment variable (e.g., [XDG_CONFIG_HOME])
74 + Default path as specified in the XDG specification
75
76 {b Example:}
77 {[
78 let xdg = Xdge.create env#fs "myapp" in
79 let config = Xdge.config_dir xdg in
80 (* config is now <fs:$HOME/.config/myapp> or the overridden path *)
81 ]}
82
83 All directories are created with permissions 0o755 if they don't exist,
84 except for runtime directories which are created with 0o700 permissions and
85 validated according to the XDG specification.
86
87 @raise Invalid_xdg_path if any environment variable contains a relative path
88*)
89
90(** {1 Accessors} *)
91
92val app_name : t -> string
93(** [app_name t] returns the application name used when creating this XDG
94 context.
95
96 This is the name that was passed to {!create} and is used as the
97 subdirectory name within each XDG base directory. *)
98
99(** {1 Base Directories} *)
100
101val config_dir : t -> Eio.Fs.dir_ty Eio.Path.t
102(** [config_dir t] returns the path to user-specific configuration files.
103
104 {b Purpose:} Store user preferences, settings, and configuration files.
105 Configuration files should be human-readable when possible.
106
107 {b Environment Variables:}
108 - [${APP_NAME}_CONFIG_DIR]: Application-specific override (highest priority)
109 - [XDG_CONFIG_HOME]: XDG standard variable
110 - Default: [$HOME/.config/{app_name}]
111
112 @see <https://specifications.freedesktop.org/basedir-spec/latest/#variables>
113 XDG_CONFIG_HOME specification *)
114
115val data_dir : t -> Eio.Fs.dir_ty Eio.Path.t
116(** [data_dir t] returns the path to user-specific data files.
117
118 {b Purpose:} Store persistent application data that should be preserved
119 across application restarts and system reboots. This data is typically not
120 modified by users directly.
121
122 {b Environment Variables:}
123 - [${APP_NAME}_DATA_DIR]: Application-specific override (highest priority)
124 - [XDG_DATA_HOME]: XDG standard variable
125 - Default: [$HOME/.local/share/{app_name}]
126
127 {b Example Files:}
128 - Application databases
129 - User-generated content (documents, projects)
130 - Downloaded resources
131 - Application plugins or extensions
132
133 @see <https://specifications.freedesktop.org/basedir-spec/latest/#variables>
134 XDG_DATA_HOME specification *)
135
136val cache_dir : t -> Eio.Fs.dir_ty Eio.Path.t
137(** [cache_dir t] returns the path to user-specific cache files.
138
139 {b Purpose:} Store non-essential cached data that can be regenerated if
140 deleted. The application should remain functional if this directory is
141 cleared, though performance may be temporarily impacted.
142
143 {b Environment Variables:}
144 - [${APP_NAME}_CACHE_DIR]: Application-specific override (highest priority)
145 - [XDG_CACHE_HOME]: XDG standard variable
146 - Default: [$HOME/.cache/{app_name}]
147
148 {b Example Files:}
149 - Downloaded thumbnails and previews
150 - Compiled bytecode or object files
151 - Network response caches
152 - Temporary computation results
153
154 Users may clear cache directories to free disk space, so always check for
155 cache validity and be prepared to regenerate data.
156
157 @see <https://specifications.freedesktop.org/basedir-spec/latest/#variables>
158 XDG_CACHE_HOME specification *)
159
160val state_dir : t -> Eio.Fs.dir_ty Eio.Path.t
161(** [state_dir t] returns the path to user-specific state files.
162
163 {b Purpose:} Store persistent state data that should be preserved between
164 application restarts but is not important enough to be user data. This
165 includes application state that can be regenerated but would impact the user
166 experience if lost.
167
168 {b Environment Variables:}
169 - [${APP_NAME}_STATE_DIR]: Application-specific override (highest priority)
170 - [XDG_STATE_HOME]: XDG standard variable
171 - Default: [$HOME/.local/state/{app_name}]
172
173 {b Example Files:}
174 - Application history (recently used files, command history)
175 - Current application state (window positions, open tabs)
176 - Logs and journal files
177 - Undo/redo history
178
179 {b Comparison with other directories:}
180 - Unlike cache: State should persist between reboots
181 - Unlike data: State can be regenerated (though inconvenient)
182 - Unlike config: State changes frequently during normal use
183
184 @see <https://specifications.freedesktop.org/basedir-spec/latest/#variables>
185 XDG_STATE_HOME specification *)
186
187val runtime_dir : t -> Eio.Fs.dir_ty Eio.Path.t option
188(** [runtime_dir t] returns the path to user-specific runtime files.
189
190 {b Purpose:} Store runtime files such as sockets, named pipes, and process
191 IDs. These files are only valid for the duration of the user's login
192 session.
193
194 {b Environment Variables:}
195 - [${APP_NAME}_RUNTIME_DIR]: Application-specific override (highest
196 priority)
197 - [XDG_RUNTIME_DIR]: XDG standard variable
198 - Default: None (returns [None] if not set)
199
200 {b Required Properties (per specification):}
201 - Owned by the user with access mode 0700
202 - Bound to the user login session lifetime
203 - Located on a local filesystem (not networked)
204 - Fully-featured by the OS (supporting proper locking, etc.)
205
206 {b Example Files:}
207 - Unix domain sockets
208 - Named pipes (FIFOs)
209 - Lock files
210 - Small process communication files
211
212 This may return [None] if no suitable runtime directory is available.
213 Applications should handle this gracefully, perhaps by falling back to
214 [/tmp] with appropriate security measures.
215
216 @see <https://specifications.freedesktop.org/basedir-spec/latest/#variables>
217 XDG_RUNTIME_DIR specification *)
218
219(** {1 System Directories} *)
220
221val config_dirs : t -> Eio.Fs.dir_ty Eio.Path.t list
222(** [config_dirs t] returns search paths for system-wide configuration files.
223
224 {b Purpose:} Provide a search path for configuration files that are shared
225 between multiple users. Files in user-specific {!config_dir} take precedence
226 over these system directories.
227
228 {b Environment Variables:}
229 - [${APP_NAME}_CONFIG_DIRS]: Application-specific override (highest
230 priority)
231 - [XDG_CONFIG_DIRS]: XDG standard variable (colon-separated list)
232 - Default: [[/etc/xdg/{app_name}]]
233
234 {b Search Order:} Directories are ordered by preference, with earlier
235 entries taking precedence over later ones. When looking for a configuration
236 file, search {!config_dir} first, then each directory in this list.
237
238 @see <https://specifications.freedesktop.org/basedir-spec/latest/#variables>
239 XDG_CONFIG_DIRS specification *)
240
241val data_dirs : t -> Eio.Fs.dir_ty Eio.Path.t list
242(** [data_dirs t] returns search paths for system-wide data files.
243
244 {b Purpose:} Provide a search path for data files that are shared between
245 multiple users. Files in user-specific {!data_dir} take precedence over
246 these system directories.
247
248 {b Environment Variables:}
249 - [${APP_NAME}_DATA_DIRS]: Application-specific override (highest priority)
250 - [XDG_DATA_DIRS]: XDG standard variable (colon-separated list)
251 - Default: [[/usr/local/share/{app_name}; /usr/share/{app_name}]]
252
253 {b Search Order:} Directories are ordered by preference, with earlier
254 entries taking precedence over later ones. When looking for a data file,
255 search {!data_dir} first, then each directory in this list.
256
257 {b Example Files:}
258 - Application icons and themes
259 - Desktop files
260 - Shared application resources
261 - Documentation files
262 - Default templates
263
264 @see <https://specifications.freedesktop.org/basedir-spec/latest/#variables>
265 XDG_DATA_DIRS specification *)
266
267(** {1 File Search} *)
268
269val find_config_file : t -> string -> Eio.Fs.dir_ty Eio.Path.t option
270(** [find_config_file t filename] searches for a configuration file following
271 XDG precedence.
272
273 This function searches for the given filename in the user configuration
274 directory first, then in system configuration directories in order of
275 preference. Files that are inaccessible (due to permissions, non-existence,
276 etc.) are silently skipped as per the XDG specification.
277
278 @param t The XDG context
279 @param filename The name of the file to search for
280 @return [Some path] if found, [None] if not found in any directory
281
282 {b Search Order:} 1. User config directory ({!config_dir}) 2. System config
283 directories ({!config_dirs}) in preference order *)
284
285val find_data_file : t -> string -> Eio.Fs.dir_ty Eio.Path.t option
286(** [find_data_file t filename] searches for a data file following XDG
287 precedence.
288
289 This function searches for the given filename in the user data directory
290 first, then in system data directories in order of preference. Files that
291 are inaccessible (due to permissions, non-existence, etc.) are silently
292 skipped as per the XDG specification.
293
294 @param t The XDG context
295 @param filename The name of the file to search for
296 @return [Some path] if found, [None] if not found in any directory
297
298 {b Search Order:} 1. User data directory ({!data_dir}) 2. System data
299 directories ({!data_dirs}) in preference order *)
300
301(** {1 Pretty Printing} *)
302
303val pp : ?brief:bool -> ?sources:bool -> Format.formatter -> t -> unit
304(** [pp ?brief ?sources ppf t] pretty prints the XDG directory configuration.
305
306 @param brief If [true], prints a compact one-line summary (default: [false])
307 @param sources
308 If [true], shows the source of each directory value, indicating whether it
309 came from defaults, environment variables, or command line (default:
310 [false])
311 @param ppf The formatter to print to
312 @param t The XDG context to print
313
314 {b Output formats:}
315 - Normal: Multi-line detailed view of all directories
316 - Brief: Single line showing app name and key directories
317 - With sources: Adds annotations showing where each path came from *)
318
319(** {1 Cmdliner Integration} *)
320
321module Cmd : sig
322 (** The type of the outer XDG context *)
323 type xdg_t = t
324 (** Cmdliner integration for XDG directory configuration.
325
326 This module provides integration with the Cmdliner library, allowing XDG
327 directories to be configured via command-line arguments while respecting
328 the precedence of environment variables. *)
329
330 type t
331 (** Type of XDG configuration gathered from command-line and environment.
332
333 This contains all XDG directory paths along with their sources, as
334 determined by command-line arguments and environment variables. *)
335
336 type dir =
337 [ `Config (** User configuration files *)
338 | `Cache (** User-specific cached data *)
339 | `Data (** User-specific application data *)
340 | `State (** User-specific state data (logs, history, etc.) *)
341 | `Runtime (** User-specific runtime files (sockets, pipes, etc.) *) ]
342 (** XDG directory types for specifying which directories an application needs.
343
344 These allow applications to declare which XDG directories they use,
345 enabling runtime systems to only provide the requested directories. *)
346
347 val term :
348 string ->
349 Eio.Fs.dir_ty Eio.Path.t ->
350 ?dirs:dir list ->
351 unit ->
352 (xdg_t * t) Cmdliner.Term.t
353 (** [term app_name fs ?dirs ()] creates a Cmdliner term for XDG directory
354 configuration.
355
356 This function generates a Cmdliner term that handles XDG directory
357 configuration through both command-line flags and environment variables,
358 and directly returns the XDG context. Only command-line flags for the
359 requested directories are generated.
360
361 @param app_name
362 The application name (used for environment variable prefixes)
363 @param fs The Eio filesystem to use for path resolution
364 @param dirs
365 List of directories to include flags for (default: all directories)
366
367 {b Generated Command-line Flags:} Only the flags for requested directories
368 are generated:
369 - [--config-dir DIR]: Override configuration directory (if [`Config] in
370 dirs)
371 - [--data-dir DIR]: Override data directory (if [`Data] in dirs)
372 - [--cache-dir DIR]: Override cache directory (if [`Cache] in dirs)
373 - [--state-dir DIR]: Override state directory (if [`State] in dirs)
374 - [--runtime-dir DIR]: Override runtime directory (if [`Runtime] in dirs)
375
376 {b Environment Variable Precedence:} For each directory type, the
377 following precedence applies:
378 + Command-line flag (e.g., [--config-dir]) - if enabled
379 + Application-specific variable (e.g., [MYAPP_CONFIG_DIR])
380 + XDG standard variable (e.g., [XDG_CONFIG_HOME])
381 + Default value *)
382
383 val cache_term : string -> string Cmdliner.Term.t
384 (** [cache_term app_name] creates a Cmdliner term that provides just the cache
385 directory path as a string, respecting XDG precedence.
386
387 This is a convenience function for applications that only need cache
388 directory configuration. It returns the resolved cache directory path
389 directly as a string, suitable for use in other Cmdliner terms.
390
391 @param app_name
392 The application name (used for environment variable prefixes)
393
394 {b Generated Command-line Flag:}
395 - [--cache-dir DIR]: Override cache directory
396
397 {b Environment Variable Precedence:}
398 + Command-line flag ([--cache-dir])
399 + Application-specific variable (e.g., [MYAPP_CACHE_DIR])
400 + XDG standard variable ([XDG_CACHE_HOME])
401 + Default value ([$HOME/.cache/{app_name}]) *)
402
403 val env_docs : string -> string
404 (** [env_docs app_name] generates documentation for environment variables.
405
406 Returns a formatted string documenting all environment variables that
407 affect XDG directory configuration for the given application. This is
408 useful for generating man pages or help text.
409
410 @param app_name The application name
411 @return A formatted documentation string
412
413 {b Included Information:}
414 - Configuration precedence rules
415 - Application-specific environment variables
416 - XDG standard environment variables
417 - Default values for each directory type *)
418
419 val pp : Format.formatter -> t -> unit
420 (** [pp ppf config] pretty prints a Cmdliner configuration.
421
422 This function formats the configuration showing each directory path along
423 with its source, which is helpful for debugging configuration issues or
424 displaying the current configuration to users.
425
426 @param ppf The formatter to print to
427 @param config The configuration to print *)
428end