···1+(*
2+ * ISC License
3+ *
4+ * Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>
5+ *
6+ * Permission to use, copy, modify, and distribute this software for any
7+ * purpose with or without fee is hereby granted, provided that the above
8+ * copyright notice and this permission notice appear in all copies.
9+ *
10+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17+ *
18+ *)
+30
dune-project
···000000000000000000000000000000
···1+(lang dune 3.20)
2+3+(name xdge)
4+5+(generate_opam_files true)
6+7+(license ISC)
8+(authors "Anil Madhavapeddy")
9+(homepage "https://tangled.sh/@anil.recoil.org/xdge")
10+(maintainers "Anil Madhavapeddy <anil@recoil.org>")
11+(bug_reports "https://tangled.sh/@anil.recoil.org/xgde/issues")
12+(maintenance_intent "(latest)")
13+14+(package
15+ (name xdge)
16+ (synopsis "XDG Base Directory Specification support for Eio")
17+ (description
18+ "This library implements the XDG Base Directory Specification \
19+ with Eio capabilities to provide safe access to configuration, \
20+ data, cache, state, and runtime directories. The library exposes \
21+ Cmdliner terms that allow for proper environment variable overrides \
22+ and command-line flags.")
23+ (depends
24+ (ocaml (>= 5.1.0))
25+ (eio (>= 1.1))
26+ (cmdliner (>= 1.2.0))
27+ (fmt (>= 0.11.0))
28+ (odoc :with-doc)
29+ (eio_main :with-test)
30+ (alcotest (and :with-test (>= 1.7.0)))))
···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+41+type 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+51+exception 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+59+val 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+92+val 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+101+val 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+115+val 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+136+val 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+160+val 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+187+val 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+221+val 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+241+val 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+269+val 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+285+val 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+303+val 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+321+module 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 *)
428+end