this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

more

-2554
cacheio/_build/.db

This is a binary file and will not be displayed.

cacheio/_build/.digest-db

This is a binary file and will not be displayed.

-1
cacheio/_build/.filesystem-clock
··· 1 - <dummy>
cacheio/_build/.lock

This is a binary file and will not be displayed.

-2
cacheio/_build/default/.dune/configurator
··· 1 - (ocamlc /Users/avsm/.opam/5.3.0/bin/ocamlc.opt) 2 - (ocaml_config_vars (afl_instrument false) (architecture arm64) (asm "cc -c -Wno-trigraphs") (asm_cfi_supported true) (ast_impl_magic_number Caml1999M035) (ast_intf_magic_number Caml1999N035) (bytecode_cflags "-O2 -fno-strict-aliasing -fwrapv -pthread ") (bytecode_cppflags " -D_FILE_OFFSET_BITS=64 ") (bytecomp_c_compiler "cc -O2 -fno-strict-aliasing -fwrapv -pthread -D_FILE_OFFSET_BITS=64 ") (bytecomp_c_libraries "-L/opt/homebrew/opt/zstd/lib -lzstd -lpthread") (c_compiler cc) (ccomp_type cc) (cma_magic_number Caml1999A035) (cmi_magic_number Caml1999I035) (cmo_magic_number Caml1999O035) (cmt_magic_number Caml1999T035) (cmx_magic_number Caml1999Y035) (cmxa_magic_number Caml1999Z035) (cmxs_magic_number Caml1999D035) (default_executable_name a.out) (default_safe_string true) (exec_magic_number Caml1999X035) (ext_asm .s) (ext_dll .so) (ext_exe "") (ext_lib .a) (ext_obj .o) (flambda false) (flat_float_array true) (function_sections false) (host aarch64-apple-darwin24.6.0) (int_size 63) (linear_magic_number Caml1999L035) (model default) (naked_pointers false) (native_c_compiler "cc -O2 -fno-strict-aliasing -fwrapv -pthread -D_FILE_OFFSET_BITS=64 ") (native_c_libraries " -lpthread") (native_cflags "-O2 -fno-strict-aliasing -fwrapv -pthread ") (native_compiler true) (native_cppflags " -D_FILE_OFFSET_BITS=64 ") (native_dynlink true) (native_ldflags "") (native_pack_linker "ld -r -o ") (ocamlc_cflags "-O2 -fno-strict-aliasing -fwrapv -pthread ") (ocamlc_cppflags " -D_FILE_OFFSET_BITS=64 ") (ocamlopt_cflags "-O2 -fno-strict-aliasing -fwrapv -pthread ") (ocamlopt_cppflags " -D_FILE_OFFSET_BITS=64 ") (os_type Unix) (safe_string true) (standard_library /Users/avsm/.opam/5.3.0/lib/ocaml) (standard_library_default /Users/avsm/.opam/5.3.0/lib/ocaml) (supports_shared_libraries true) (system macosx) (systhread_supported true) (target aarch64-apple-darwin24.6.0) (tsan false) (version 5.3.0) (windows_unicode false) (with_frame_pointers false) (word_size 64))
-1
cacheio/_build/default/.dune/configurator.v2
··· 1 - ((6:ocamlc38:/Users/avsm/.opam/5.3.0/bin/ocamlc.opt)(17:ocaml_config_vars((14:afl_instrument5:false)(12:architecture5:arm64)(3:asm20:cc -c -Wno-trigraphs)(17:asm_cfi_supported4:true)(21:ast_impl_magic_number12:Caml1999M035)(21:ast_intf_magic_number12:Caml1999N035)(15:bytecode_cflags43:-O2 -fno-strict-aliasing -fwrapv -pthread )(17:bytecode_cppflags24: -D_FILE_OFFSET_BITS=64 )(19:bytecomp_c_compiler71:cc -O2 -fno-strict-aliasing -fwrapv -pthread -D_FILE_OFFSET_BITS=64 )(20:bytecomp_c_libraries48:-L/opt/homebrew/opt/zstd/lib -lzstd -lpthread)(10:c_compiler2:cc)(10:ccomp_type2:cc)(16:cma_magic_number12:Caml1999A035)(16:cmi_magic_number12:Caml1999I035)(16:cmo_magic_number12:Caml1999O035)(16:cmt_magic_number12:Caml1999T035)(16:cmx_magic_number12:Caml1999Y035)(17:cmxa_magic_number12:Caml1999Z035)(17:cmxs_magic_number12:Caml1999D035)(23:default_executable_name5:a.out)(19:default_safe_string4:true)(17:exec_magic_number12:Caml1999X035)(7:ext_asm2:.s)(7:ext_dll3:.so)(7:ext_exe0:)(7:ext_lib2:.a)(7:ext_obj2:.o)(7:flambda5:false)(16:flat_float_array4:true)(17:function_sections5:false)(4:host26:aarch64-apple-darwin24.6.0)(8:int_size2:63)(19:linear_magic_number12:Caml1999L035)(5:model7:default)(14:naked_pointers5:false)(17:native_c_compiler71:cc -O2 -fno-strict-aliasing -fwrapv -pthread -D_FILE_OFFSET_BITS=64 )(18:native_c_libraries12: -lpthread)(13:native_cflags43:-O2 -fno-strict-aliasing -fwrapv -pthread )(15:native_compiler4:true)(15:native_cppflags24: -D_FILE_OFFSET_BITS=64 )(14:native_dynlink4:true)(14:native_ldflags0:)(18:native_pack_linker9:ld -r -o )(13:ocamlc_cflags43:-O2 -fno-strict-aliasing -fwrapv -pthread )(15:ocamlc_cppflags24: -D_FILE_OFFSET_BITS=64 )(15:ocamlopt_cflags43:-O2 -fno-strict-aliasing -fwrapv -pthread )(17:ocamlopt_cppflags24: -D_FILE_OFFSET_BITS=64 )(7:os_type4:Unix)(11:safe_string4:true)(16:standard_library33:/Users/avsm/.opam/5.3.0/lib/ocaml)(24:standard_library_default33:/Users/avsm/.opam/5.3.0/lib/ocaml)(25:supports_shared_libraries4:true)(6:system6:macosx)(19:systhread_supported4:true)(6:target26:aarch64-apple-darwin24.6.0)(4:tsan5:false)(7:version5:5.3.0)(15:windows_unicode5:false)(19:with_frame_pointers5:false)(9:word_size2:64))))
-6
cacheio/_build/default/META.cacheio
··· 1 - description = "" 2 - requires = "cmdliner eio fmt logs ptime ptime.clock ptime.clock.os yojson" 3 - archive(byte) = "cacheio.cma" 4 - archive(native) = "cacheio.cmxa" 5 - plugin(byte) = "cacheio.cma" 6 - plugin(native) = "cacheio.cmxs"
-183
cacheio/_build/default/README.md
··· 1 - # CacheIO 2 - 3 - A flexible file-based caching library for OCaml with XDG Base Directory support, TTL-based expiration, and built-in cmdliner integration. 4 - 5 - ## Features 6 - 7 - - **File-based and Memory Storage**: Choose between persistent file storage or fast in-memory caching 8 - - **XDG Compliance**: Follows XDG Base Directory specification for cache locations 9 - - **TTL-based Expiration**: Automatic expiration of cached entries 10 - - **JSON Support**: Built-in JSON serialization/deserialization 11 - - **Hierarchical Keys**: Support for path-like cache keys 12 - - **Cmdliner Integration**: Ready-to-use command-line options for cache management 13 - - **Thread-safe**: Safe concurrent access using Eio mutexes 14 - - **Statistics**: Track cache hits, misses, size, and entry count 15 - 16 - ## Installation 17 - 18 - ```bash 19 - opam install cacheio 20 - ``` 21 - 22 - Or add to your `dune-project`: 23 - 24 - ```dune 25 - (package 26 - (name myapp) 27 - (depends 28 - cacheio 29 - ...)) 30 - ``` 31 - 32 - ## Basic Usage 33 - 34 - ```ocaml 35 - open Cacheio 36 - 37 - let main () = 38 - Eio_main.run @@ fun env -> 39 - 40 - (* Create file-based cache *) 41 - let base_dir = Eio.Path.(Eio.Stdenv.fs env / "/tmp/cache") in 42 - let storage = FileStorage.create ~base_dir ~app_name:"myapp" () in 43 - let cache = create ~storage:(`File storage) in 44 - 45 - (* Store and retrieve data *) 46 - put cache ~key:"user:123" ~data:"John Doe" ~ttl:3600.0; 47 - 48 - match get cache ~key:"user:123" with 49 - | Some data -> Printf.printf "Found: %s\n" data 50 - | None -> Printf.printf "Not found or expired\n" 51 - ``` 52 - 53 - ## JSON Support 54 - 55 - ```ocaml 56 - (* Store JSON data *) 57 - let user_data = `Assoc [ 58 - ("id", `Int 123); 59 - ("name", `String "John Doe"); 60 - ("email", `String "john@example.com") 61 - ] in 62 - 63 - put_json cache ~key:"user:123" ~data:user_data ~ttl:3600.0; 64 - 65 - (* Retrieve and parse JSON *) 66 - match get_json cache ~key:"user:123" with 67 - | Some json -> (* Handle JSON data *) 68 - | None -> (* Handle cache miss *) 69 - ``` 70 - 71 - ## Hierarchical Keys 72 - 73 - ```ocaml 74 - (* Build structured cache keys *) 75 - let key = make_key ["github"; "ocaml"; "ocaml"; "issues"; "1234"] in 76 - (* Results in: "github/ocaml/ocaml/issues/1234" *) 77 - 78 - (* Split keys back into components *) 79 - let components = split_key key in 80 - (* Returns: ["github"; "ocaml"; "ocaml"; "issues"; "1234"] *) 81 - ``` 82 - 83 - ## Cmdliner Integration 84 - 85 - Add cache management options to your CLI application: 86 - 87 - ```ocaml 88 - open Cmdliner 89 - 90 - let main cache_config = 91 - Eio_main.run @@ fun env -> 92 - 93 - (* Setup cache with CLI configuration *) 94 - match Cacheio.Cmdliner.setup_cache ~env ~app_name:"myapp" ~config:cache_config with 95 - | None -> print_endline "Caching disabled" 96 - | Some cache -> 97 - (* Use cache in your application *) 98 - ... 99 - 100 - let cmd = 101 - let doc = "My application with caching" in 102 - let info = Cmd.info "myapp" ~doc in 103 - Cmd.v info Term.(const main $ Cacheio.Cmdliner.cache_config) 104 - 105 - let () = exit (Cmd.eval cmd) 106 - ``` 107 - 108 - This provides the following CLI options: 109 - - `--cache-dir DIR`: Override cache directory 110 - - `--no-cache`: Disable caching 111 - - `--cache-clear`: Clear cache before running 112 - - `--cache-stats`: Show cache statistics 113 - - `--cache-max-age HOURS`: Set maximum cache age 114 - 115 - ## XDG Support 116 - 117 - The library follows XDG Base Directory specification: 118 - 119 - ```ocaml 120 - (* Get standard directories *) 121 - let cache_home = Cacheio.XDG.cache_home () in 122 - (* Returns: $XDG_CACHE_HOME or ~/.cache *) 123 - 124 - let app_cache = Cacheio.XDG.app_cache_dir ~app_name:"myapp" in 125 - (* Returns: $XDG_CACHE_HOME/myapp or ~/.cache/myapp *) 126 - ``` 127 - 128 - ## Memory Storage 129 - 130 - For temporary caching without disk persistence: 131 - 132 - ```ocaml 133 - let storage = MemoryStorage.create ~max_entries:1000 () in 134 - let cache = create ~storage:(`Memory storage) in 135 - 136 - (* Use same interface as file storage *) 137 - put cache ~key:"temp" ~data:"value" ~ttl:60.0 138 - ``` 139 - 140 - ## Statistics 141 - 142 - Monitor cache performance: 143 - 144 - ```ocaml 145 - let stats = stats cache in 146 - Printf.printf "Cache hits: %d\n" stats.hits; 147 - Printf.printf "Cache misses: %d\n" stats.misses; 148 - Printf.printf "Total size: %s\n" (format_size stats.size); 149 - Printf.printf "Entries: %d\n" stats.entries 150 - ``` 151 - 152 - ## Integration with Requests Library 153 - 154 - The cacheio library can be used with the requests library for HTTP caching: 155 - 156 - ```ocaml 157 - (* In requests library *) 158 - module Cache = struct 159 - type 'a t = Cacheio.t 160 - 161 - let create ~max_size:_ () = 162 - let storage = Cacheio.MemoryStorage.create ~max_entries:max_size () in 163 - Cacheio.create ~storage:(`Memory storage) 164 - 165 - let get t ~method_ ~url ~headers:_ = 166 - if method_ = `GET then 167 - match Cacheio.get t ~key:(Uri.to_string url) with 168 - | Some data -> 169 - (* Deserialize response *) 170 - Some (deserialize_response data) 171 - | None -> None 172 - else None 173 - 174 - let put t ~method_ ~url ~response = 175 - if method_ = `GET && Response.is_success response then 176 - let data = serialize_response response in 177 - Cacheio.put t ~key:(Uri.to_string url) ~data ~ttl:3600.0 178 - end 179 - ``` 180 - 181 - ## License 182 - 183 - MIT
cacheio/_build/default/bin/.example.eobjs/byte/dune__exe__Example.cmi

This is a binary file and will not be displayed.

cacheio/_build/default/bin/.example.eobjs/byte/dune__exe__Example.cmo

This is a binary file and will not be displayed.

cacheio/_build/default/bin/.example.eobjs/byte/dune__exe__Example.cmt

This is a binary file and will not be displayed.

cacheio/_build/default/bin/.example.eobjs/byte/dune__exe__Example.cmti

This is a binary file and will not be displayed.

cacheio/_build/default/bin/.example.eobjs/native/dune__exe__Example.cmx

This is a binary file and will not be displayed.

cacheio/_build/default/bin/.example.eobjs/native/dune__exe__Example.o

This is a binary file and will not be displayed.

cacheio/_build/default/bin/.merlin-conf/exe-example

This is a binary file and will not be displayed.

cacheio/_build/default/bin/example.exe

This is a binary file and will not be displayed.

-118
cacheio/_build/default/bin/example.ml
··· 1 - (** Example usage of the CacheIO library *) 2 - 3 - open Cacheio 4 - 5 - let setup_logs () = 6 - Logs.set_level (Some Logs.Info); 7 - Logs.set_reporter (Logs_fmt.reporter ()) 8 - 9 - (** Example: Using CacheIO for HTTP responses *) 10 - let http_cache_example cache = 11 - Logs.app (fun m -> m "HTTP Cache Example"); 12 - Logs.app (fun m -> m "=================="); 13 - 14 - (* Simulate caching HTTP responses *) 15 - let urls = [ 16 - "https://api.example.com/users"; 17 - "https://api.example.com/posts"; 18 - "https://api.example.com/comments"; 19 - ] in 20 - 21 - (* Store some responses *) 22 - List.iteri (fun i url -> 23 - let response_data = `Assoc [ 24 - ("status", `Int 200); 25 - ("body", `String (Printf.sprintf "Response body for request %d" i)); 26 - ("headers", `Assoc [ 27 - ("content-type", `String "application/json"); 28 - ("cache-control", `String "max-age=3600"); 29 - ]); 30 - ] in 31 - 32 - (* Cache for 1 hour *) 33 - Cacheio.put_json cache ~key:url ~data:response_data ~ttl:3600.0; 34 - Logs.info (fun m -> m "Cached response for %s" url) 35 - ) urls; 36 - 37 - (* Retrieve cached responses *) 38 - List.iter (fun url -> 39 - match Cacheio.get_json cache ~key:url with 40 - | Some json -> 41 - Logs.app (fun m -> m "Cache HIT for %s:" url); 42 - Logs.app (fun m -> m " %s" (Yojson.Safe.pretty_to_string json)) 43 - | None -> 44 - Logs.app (fun m -> m "Cache MISS for %s" url) 45 - ) urls; 46 - 47 - (* Show statistics *) 48 - let stats = Cacheio.stats cache in 49 - Logs.app (fun m -> m "\nCache Statistics:"); 50 - Logs.app (fun m -> m " Hits: %d" stats.hits); 51 - Logs.app (fun m -> m " Misses: %d" stats.misses); 52 - Logs.app (fun m -> m " Entries: %d" stats.entries); 53 - Logs.app (fun m -> m " Size: %s" (Cacheio.format_size stats.size)) 54 - 55 - (** Example: Using hierarchical keys *) 56 - let hierarchical_example cache = 57 - Logs.app (fun m -> m "\nHierarchical Keys Example"); 58 - Logs.app (fun m -> m "========================"); 59 - 60 - (* Cache GitHub repository data with hierarchical keys *) 61 - let repos = [ 62 - ["github"; "ocaml"; "ocaml"; "issues"; "1234"]; 63 - ["github"; "mirage"; "mirage"; "pulls"; "567"]; 64 - ["gitlab"; "company"; "project"; "commits"; "abc123"]; 65 - ] in 66 - 67 - List.iter (fun components -> 68 - let key = Cacheio.make_key components in 69 - let data = Printf.sprintf "Data for %s" key in 70 - Cacheio.put cache ~key ~data ~ttl:1800.0; 71 - Logs.info (fun m -> m "Stored data with key: %s" key) 72 - ) repos; 73 - 74 - (* List all keys *) 75 - Logs.app (fun m -> m "\nAll cached keys:"); 76 - Cacheio.list_keys cache |> List.iter (fun key -> 77 - Logs.app (fun m -> m " - %s" key) 78 - ) 79 - 80 - (** Main entry point *) 81 - let main () = 82 - setup_logs (); 83 - 84 - Eio_main.run @@ fun env -> 85 - 86 - (* Create cache in /tmp for demo *) 87 - let base_dir = Eio.Path.(Eio.Stdenv.fs env / "/tmp") in 88 - (* Ensure base directory exists *) 89 - (try Eio.Path.mkdir ~perm:0o755 Eio.Path.(base_dir / "cacheio-demo") with _ -> ()); 90 - let base_dir = Eio.Path.(base_dir / "cacheio-demo") in 91 - let storage = FileStorage.create ~base_dir ~app_name:"example" () in 92 - let cache = Cacheio.create ~storage:(`File storage) in 93 - 94 - (* Clear cache first for demo *) 95 - Cacheio.clear cache; 96 - 97 - (* Run examples *) 98 - http_cache_example cache; 99 - hierarchical_example cache; 100 - 101 - (* Test memory storage as well *) 102 - Logs.app (fun m -> m "\n\nMemory Storage Example"); 103 - Logs.app (fun m -> m "====================="); 104 - let mem_storage = MemoryStorage.create ~max_entries:10 () in 105 - let mem_cache = Cacheio.create ~storage:(`Memory mem_storage) in 106 - 107 - (* Store some data *) 108 - for i = 1 to 15 do 109 - let key = Printf.sprintf "key_%d" i in 110 - let data = Printf.sprintf "value_%d" i in 111 - Cacheio.put mem_cache ~key ~data ~ttl:60.0 112 - done; 113 - 114 - (* Check that eviction worked (max 10 entries) *) 115 - let stats = Cacheio.stats mem_cache in 116 - Logs.app (fun m -> m "Memory cache entries: %d (max: 10)" stats.entries) 117 - 118 - let () = main ()
-1
cacheio/_build/default/bin/example.mli
··· 1 - (* Auto-generated by Dune *)
-34
cacheio/_build/default/cacheio.dune-package
··· 1 - (lang dune 3.20) 2 - (name cacheio) 3 - (sections (lib .) (libexec .) (bin ../../bin) (doc ../../doc/cacheio)) 4 - (files 5 - (lib 6 - (META 7 - cacheio.a 8 - cacheio.cma 9 - cacheio.cmi 10 - cacheio.cmt 11 - cacheio.cmti 12 - cacheio.cmx 13 - cacheio.cmxa 14 - cacheio.ml 15 - cacheio.mli 16 - dune-package 17 - opam)) 18 - (libexec (cacheio.cmxs)) 19 - (bin (cacheio-example)) 20 - (doc (README.md))) 21 - (library 22 - (name cacheio) 23 - (kind normal) 24 - (archives (byte cacheio.cma) (native cacheio.cmxa)) 25 - (plugins (byte cacheio.cma) (native cacheio.cmxs)) 26 - (native_archives cacheio.a) 27 - (requires eio cmdliner yojson ptime ptime.clock ptime.clock.os logs fmt) 28 - (main_module_name Cacheio) 29 - (modes byte native) 30 - (modules 31 - (singleton 32 - (obj_name cacheio) 33 - (visibility public) 34 - (source (path Cacheio) (intf (path cacheio.mli)) (impl (path cacheio.ml))))))
-23
cacheio/_build/default/cacheio.install
··· 1 - lib: [ 2 - "_build/install/default/lib/cacheio/META" 3 - "_build/install/default/lib/cacheio/cacheio.a" 4 - "_build/install/default/lib/cacheio/cacheio.cma" 5 - "_build/install/default/lib/cacheio/cacheio.cmi" 6 - "_build/install/default/lib/cacheio/cacheio.cmt" 7 - "_build/install/default/lib/cacheio/cacheio.cmti" 8 - "_build/install/default/lib/cacheio/cacheio.cmx" 9 - "_build/install/default/lib/cacheio/cacheio.cmxa" 10 - "_build/install/default/lib/cacheio/cacheio.ml" 11 - "_build/install/default/lib/cacheio/cacheio.mli" 12 - "_build/install/default/lib/cacheio/dune-package" 13 - "_build/install/default/lib/cacheio/opam" 14 - ] 15 - libexec: [ 16 - "_build/install/default/lib/cacheio/cacheio.cmxs" 17 - ] 18 - bin: [ 19 - "_build/install/default/bin/cacheio-example" 20 - ] 21 - doc: [ 22 - "_build/install/default/doc/cacheio/README.md" 23 - ]
-39
cacheio/_build/default/cacheio.opam
··· 1 - # This file is generated by dune, edit dune-project instead 2 - opam-version: "2.0" 3 - synopsis: "A flexible file-based caching library for OCaml with XDG support" 4 - description: 5 - "CacheIO provides a simple and efficient file-based caching system with support for XDG base directory specification, TTL-based expiration, and cmdliner integration for CLI applications." 6 - maintainer: ["Your Name"] 7 - authors: ["Your Name"] 8 - license: "MIT" 9 - tags: ["cache" "filesystem" "xdg" "cmdliner" "eio"] 10 - homepage: "https://github.com/username/cacheio" 11 - doc: "https://url/to/documentation" 12 - bug-reports: "https://github.com/username/cacheio/issues" 13 - depends: [ 14 - "ocaml" 15 - "dune" {>= "3.16"} 16 - "eio" 17 - "cmdliner" {>= "1.3.0"} 18 - "yojson" 19 - "ptime" 20 - "logs" 21 - "fmt" 22 - "alcotest" {with-test} 23 - "odoc" {with-doc} 24 - ] 25 - build: [ 26 - ["dune" "subst"] {dev} 27 - [ 28 - "dune" 29 - "build" 30 - "-p" 31 - name 32 - "-j" 33 - jobs 34 - "@install" 35 - "@runtest" {with-test} 36 - "@doc" {with-doc} 37 - ] 38 - ] 39 - dev-repo: "git+https://github.com/username/cacheio.git"
cacheio/_build/default/lib/.cacheio.objs/byte/cacheio.cmi

This is a binary file and will not be displayed.

cacheio/_build/default/lib/.cacheio.objs/byte/cacheio.cmt

This is a binary file and will not be displayed.

cacheio/_build/default/lib/.cacheio.objs/byte/cacheio.cmti

This is a binary file and will not be displayed.

cacheio/_build/default/lib/.merlin-conf/lib-cacheio

This is a binary file and will not be displayed.

cacheio/_build/default/lib/cacheio.a

This is a binary file and will not be displayed.

cacheio/_build/default/lib/cacheio.cma

This is a binary file and will not be displayed.

cacheio/_build/default/lib/cacheio.cmxa

This is a binary file and will not be displayed.

cacheio/_build/default/lib/cacheio.cmxs

This is a binary file and will not be displayed.

-528
cacheio/_build/default/lib/cacheio.ml
··· 1 - (** CacheIO - A flexible file-based caching library for OCaml with XDG support *) 2 - 3 - open Eio 4 - 5 - (** Cache statistics *) 6 - type stats = { 7 - hits : int; 8 - misses : int; 9 - size : int64; 10 - entries : int; 11 - } 12 - 13 - (** Cache storage backend signature *) 14 - module type STORAGE = sig 15 - type t 16 - val get : t -> key:string -> string option 17 - val put : t -> key:string -> data:string -> ttl:float -> unit 18 - val delete : t -> key:string -> unit 19 - val clear : t -> unit 20 - val exists : t -> key:string -> bool 21 - val stats : t -> stats 22 - val list_keys : t -> string list 23 - end 24 - 25 - (** {2 File-based Storage Implementation} *) 26 - 27 - module FileStorage = struct 28 - type t = { 29 - base_dir : Fs.dir_ty Path.t; 30 - max_size : int64 option; 31 - app_name : string; 32 - mutable hits : int; 33 - mutable misses : int; 34 - mutex : Eio.Mutex.t; 35 - } 36 - 37 - let create ~base_dir ?max_size ~app_name () = 38 - { base_dir; max_size; app_name; hits = 0; misses = 0; 39 - mutex = Eio.Mutex.create () } 40 - 41 - (** Ensure directory structure exists. TODO simplify with create_dirs? *) 42 - let ensure_dir_exists dir path_components = 43 - let rec ensure components current_path = 44 - match components with 45 - | [] -> () 46 - | component :: rest -> 47 - let new_path = Path.(current_path / component) in 48 - (try 49 - Path.mkdir ~perm:0o755 new_path 50 - with 51 - | Eio.Io (Eio.Fs.E (Already_exists _), _) -> () 52 - | Eio.Io (Eio.Fs.E (Not_found _), _) -> 53 - (* Parent might not exist, create it first *) 54 - (match components with 55 - | [] -> () 56 - | _ -> 57 - (try Path.mkdir ~perm:0o755 current_path with _ -> ()); 58 - Path.mkdir ~perm:0o755 new_path) 59 - | e -> raise e); 60 - ensure rest new_path 61 - in 62 - ensure path_components dir 63 - 64 - (** Convert cache key to filesystem path. 65 - TODO or hash the key and split into subdir chunks instead of this. *) 66 - let key_to_path key = 67 - (* Replace problematic characters for filesystem *) 68 - String.map (function 69 - | '/' -> '_' 70 - | ':' -> '-' 71 - | '?' | '&' | '=' -> '_' 72 - | c -> c 73 - ) key 74 - 75 - let get_cache_path t ~key = 76 - let safe_key = key_to_path key in 77 - (* Use first 2 chars for sharding if key is long enough *) 78 - let path_components = 79 - if String.length safe_key >= 2 then 80 - [t.app_name; String.sub safe_key 0 2] 81 - else 82 - [t.app_name] 83 - in 84 - ensure_dir_exists t.base_dir path_components; 85 - 86 - (* Build final path *) 87 - let dir_path = List.fold_left (fun acc comp -> 88 - Path.(acc / comp)) t.base_dir path_components in 89 - Path.(dir_path / (safe_key ^ ".cache")) 90 - 91 - (** Cache entry metadata *) 92 - type entry_metadata = { 93 - created_at : Ptime.t; 94 - ttl : float; 95 - size : int; 96 - } 97 - 98 - let metadata_to_json metadata = 99 - `Assoc [ 100 - ("created_at", `String (Ptime.to_rfc3339 metadata.created_at)); 101 - ("ttl", `Float metadata.ttl); 102 - ("size", `Int metadata.size); 103 - ] 104 - 105 - let metadata_from_json json = 106 - let open Yojson.Safe.Util in 107 - let created_at_str = json |> member "created_at" |> to_string in 108 - let created_at = match Ptime.of_rfc3339 created_at_str with 109 - | Ok (t, _, _) -> t 110 - | Error _ -> Ptime.epoch 111 - in 112 - { 113 - created_at; 114 - ttl = json |> member "ttl" |> to_float; 115 - size = json |> member "size" |> to_int; 116 - } 117 - 118 - let is_expired metadata = 119 - let now = Ptime_clock.now () in 120 - let age_span = Ptime.diff now metadata.created_at in 121 - let age_seconds = Ptime.Span.to_float_s age_span in 122 - age_seconds > metadata.ttl 123 - 124 - let get t ~key = 125 - Mutex.use_rw ~protect:true t.mutex @@ fun () -> 126 - let cache_path = get_cache_path t ~key in 127 - try 128 - let content = Path.load cache_path in 129 - let json = Yojson.Safe.from_string content in 130 - let open Yojson.Safe.Util in 131 - 132 - let metadata = json |> member "metadata" |> metadata_from_json in 133 - 134 - if is_expired metadata then begin 135 - t.misses <- t.misses + 1; 136 - (* Delete expired entry *) 137 - (try Path.unlink cache_path with _ -> ()); 138 - None 139 - end else begin 140 - t.hits <- t.hits + 1; 141 - Some (json |> member "data" |> to_string) 142 - end 143 - with 144 - | _ -> 145 - t.misses <- t.misses + 1; 146 - None 147 - 148 - let put t ~key ~data ~ttl = 149 - Mutex.use_rw ~protect:false t.mutex @@ fun () -> 150 - let cache_path = get_cache_path t ~key in 151 - let metadata = { 152 - created_at = Ptime_clock.now (); 153 - ttl; 154 - size = String.length data; 155 - } in 156 - 157 - let cache_entry = `Assoc [ 158 - ("metadata", metadata_to_json metadata); 159 - ("data", `String data); 160 - ] in 161 - 162 - let json_str = Yojson.Safe.to_string cache_entry in 163 - Path.save ~create:(`Or_truncate 0o644) cache_path json_str 164 - 165 - let delete t ~key = 166 - Mutex.use_rw ~protect:false t.mutex @@ fun () -> 167 - let cache_path = get_cache_path t ~key in 168 - try Path.unlink cache_path with _ -> () 169 - 170 - let clear t = 171 - Mutex.use_rw ~protect:false t.mutex @@ fun () -> 172 - (* Clear all entries in app directory *) 173 - let app_dir = Path.(t.base_dir / t.app_name) in 174 - let rec clear_dir dir = 175 - try 176 - Path.read_dir dir |> List.iter (fun name -> 177 - let path = Path.(dir / name) in 178 - try 179 - if String.ends_with ~suffix:".cache" name then 180 - Path.unlink path 181 - else 182 - clear_dir path 183 - with _ -> () 184 - ) 185 - with _ -> () 186 - in 187 - clear_dir app_dir; 188 - t.hits <- 0; 189 - t.misses <- 0 190 - 191 - let exists t ~key = 192 - match get t ~key with 193 - | Some _ -> true 194 - | None -> false 195 - 196 - let stats t = 197 - Mutex.use_rw ~protect:true t.mutex @@ fun () -> 198 - (* Calculate total size and count entries *) 199 - let app_dir = Path.(t.base_dir / t.app_name) in 200 - let total_size = ref 0L in 201 - let total_entries = ref 0 in 202 - 203 - let rec scan_dir dir = 204 - try 205 - Path.read_dir dir |> List.iter (fun name -> 206 - let path = Path.(dir / name) in 207 - try 208 - if String.ends_with ~suffix:".cache" name then begin 209 - incr total_entries; 210 - (* Try to get file size *) 211 - let content = Path.load path in 212 - total_size := Int64.add !total_size (Int64.of_int (String.length content)) 213 - end else 214 - scan_dir path 215 - with _ -> () 216 - ) 217 - with _ -> () 218 - in 219 - scan_dir app_dir; 220 - 221 - { hits = t.hits; misses = t.misses; size = !total_size; entries = !total_entries } 222 - 223 - let list_keys t = 224 - Mutex.use_rw ~protect:true t.mutex @@ fun () -> 225 - let app_dir = Path.(t.base_dir / t.app_name) in 226 - let keys = ref [] in 227 - 228 - let rec scan_dir dir prefix = 229 - try 230 - Path.read_dir dir |> List.iter (fun name -> 231 - let path = Path.(dir / name) in 232 - try 233 - if String.ends_with ~suffix:".cache" name then 234 - let key_name = String.sub name 0 (String.length name - 6) in 235 - keys := (prefix ^ key_name) :: !keys 236 - else 237 - scan_dir path (prefix ^ name ^ "/") 238 - with _ -> () 239 - ) 240 - with _ -> () 241 - in 242 - scan_dir app_dir ""; 243 - !keys 244 - 245 - let cleanup_expired t = 246 - Mutex.use_rw ~protect:false t.mutex @@ fun () -> 247 - let removed = ref 0 in 248 - list_keys t |> List.iter (fun key -> 249 - match get t ~key with 250 - | None -> incr removed (* Already cleaned up by get *) 251 - | Some _ -> () 252 - ); 253 - !removed 254 - end 255 - 256 - (** {2 Memory Storage Implementation} *) 257 - 258 - module MemoryStorage = struct 259 - type entry = { 260 - data : string; 261 - created_at : Ptime.t; 262 - ttl : float; 263 - } 264 - 265 - type t = { 266 - table : (string, entry) Hashtbl.t; 267 - max_entries : int; 268 - mutable hits : int; 269 - mutable misses : int; 270 - mutex : Eio.Mutex.t; 271 - } 272 - 273 - let create ?(max_entries=1000) () = 274 - { table = Hashtbl.create max_entries; 275 - max_entries; 276 - hits = 0; 277 - misses = 0; 278 - mutex = Eio.Mutex.create () } 279 - 280 - let is_expired entry = 281 - let now = Ptime_clock.now () in 282 - let age_span = Ptime.diff now entry.created_at in 283 - let age_seconds = Ptime.Span.to_float_s age_span in 284 - age_seconds > entry.ttl 285 - 286 - let get t ~key = 287 - Mutex.use_rw ~protect:true t.mutex @@ fun () -> 288 - match Hashtbl.find_opt t.table key with 289 - | Some entry when not (is_expired entry) -> 290 - t.hits <- t.hits + 1; 291 - Some entry.data 292 - | Some _ -> 293 - (* Remove expired entry *) 294 - Hashtbl.remove t.table key; 295 - t.misses <- t.misses + 1; 296 - None 297 - | None -> 298 - t.misses <- t.misses + 1; 299 - None 300 - 301 - let put t ~key ~data ~ttl = 302 - Mutex.use_rw ~protect:false t.mutex @@ fun () -> 303 - (* Simple eviction: remove oldest if at capacity *) 304 - if Hashtbl.length t.table >= t.max_entries then begin 305 - (* Remove first entry found (not truly LRU but simple) *) 306 - match Hashtbl.to_seq t.table |> Seq.uncons with 307 - | Some ((k, _), _) -> Hashtbl.remove t.table k 308 - | None -> () 309 - end; 310 - 311 - let entry = { data; created_at = Ptime_clock.now (); ttl } in 312 - Hashtbl.replace t.table key entry 313 - 314 - let delete t ~key = 315 - Mutex.use_rw ~protect:false t.mutex @@ fun () -> 316 - Hashtbl.remove t.table key 317 - 318 - let clear t = 319 - Mutex.use_rw ~protect:false t.mutex @@ fun () -> 320 - Hashtbl.clear t.table; 321 - t.hits <- 0; 322 - t.misses <- 0 323 - 324 - let exists t ~key = 325 - match get t ~key with 326 - | Some _ -> true 327 - | None -> false 328 - 329 - let stats t = 330 - Mutex.use_rw ~protect:true t.mutex @@ fun () -> 331 - let size = Hashtbl.fold (fun _ entry acc -> 332 - Int64.add acc (Int64.of_int (String.length entry.data)) 333 - ) t.table 0L in 334 - { hits = t.hits; 335 - misses = t.misses; 336 - size; 337 - entries = Hashtbl.length t.table } 338 - 339 - let list_keys t = 340 - Mutex.use_rw ~protect:true t.mutex @@ fun () -> 341 - Hashtbl.to_seq_keys t.table |> List.of_seq 342 - end 343 - 344 - (** {1 Main Cache Interface} *) 345 - 346 - type storage = [ 347 - | `File of FileStorage.t 348 - | `Memory of MemoryStorage.t 349 - ] 350 - 351 - type t = { storage : storage } 352 - 353 - let create ~storage = { storage } 354 - 355 - let get t ~key = 356 - match t.storage with 357 - | `File fs -> FileStorage.get fs ~key 358 - | `Memory ms -> MemoryStorage.get ms ~key 359 - 360 - let put t ~key ~data ~ttl = 361 - match t.storage with 362 - | `File fs -> FileStorage.put fs ~key ~data ~ttl 363 - | `Memory ms -> MemoryStorage.put ms ~key ~data ~ttl 364 - 365 - let delete t ~key = 366 - match t.storage with 367 - | `File fs -> FileStorage.delete fs ~key 368 - | `Memory ms -> MemoryStorage.delete ms ~key 369 - 370 - let clear t = 371 - match t.storage with 372 - | `File fs -> FileStorage.clear fs 373 - | `Memory ms -> MemoryStorage.clear ms 374 - 375 - let exists t ~key = 376 - match t.storage with 377 - | `File fs -> FileStorage.exists fs ~key 378 - | `Memory ms -> MemoryStorage.exists ms ~key 379 - 380 - let stats t = 381 - match t.storage with 382 - | `File fs -> FileStorage.stats fs 383 - | `Memory ms -> MemoryStorage.stats ms 384 - 385 - let list_keys t = 386 - match t.storage with 387 - | `File fs -> FileStorage.list_keys fs 388 - | `Memory ms -> MemoryStorage.list_keys ms 389 - 390 - (** {2 JSON Operations} *) 391 - 392 - let get_json t ~key = 393 - match get t ~key with 394 - | Some data -> (try Some (Yojson.Safe.from_string data) with _ -> None) 395 - | None -> None 396 - 397 - let put_json t ~key ~data ~ttl = 398 - let json_str = Yojson.Safe.to_string data in 399 - put t ~key ~data:json_str ~ttl 400 - 401 - (** {2 Hierarchical Keys} *) 402 - 403 - let make_key components = 404 - String.concat "/" components 405 - 406 - let split_key key = 407 - String.split_on_char '/' key 408 - 409 - (** {1 XDG Support} *) 410 - 411 - (* XDG module removed - use the xdg library directly instead *) 412 - 413 - (** {1 Cmdliner Support} *) 414 - 415 - module Cmdliner = struct 416 - open Cmdliner 417 - 418 - type config = { 419 - cache_dir : string option; 420 - no_cache : bool; 421 - cache_clear : bool; 422 - cache_stats : bool; 423 - cache_max_age : int option; 424 - } 425 - 426 - let default_config = { 427 - cache_dir = None; 428 - no_cache = false; 429 - cache_clear = false; 430 - cache_stats = false; 431 - cache_max_age = None; 432 - } 433 - 434 - let cache_dir = 435 - let doc = "Override cache directory location" in 436 - Arg.(value & opt (some dir) None & info ["cache-dir"] ~docv:"DIR" ~doc) 437 - 438 - let no_cache = 439 - let doc = "Disable caching" in 440 - Arg.(value & flag & info ["no-cache"] ~doc) 441 - 442 - let cache_clear = 443 - let doc = "Clear cache before running" in 444 - Arg.(value & flag & info ["cache-clear"] ~doc) 445 - 446 - let cache_stats = 447 - let doc = "Show cache statistics" in 448 - Arg.(value & flag & info ["cache-stats"] ~doc) 449 - 450 - let cache_max_age = 451 - let doc = "Maximum cache age in hours" in 452 - Arg.(value & opt (some int) None & info ["cache-max-age"] ~docv:"HOURS" ~doc) 453 - 454 - let cache_config = 455 - let combine cache_dir no_cache cache_clear cache_stats cache_max_age = 456 - { cache_dir; no_cache; cache_clear; cache_stats; cache_max_age } 457 - in 458 - Term.(const combine $ cache_dir $ no_cache $ cache_clear $ cache_stats $ cache_max_age) 459 - 460 - let setup_cache ~env ~app_name ~config = 461 - if config.no_cache then 462 - None 463 - else 464 - let cache_dir_path = match config.cache_dir with 465 - | Some dir -> dir 466 - | None -> 467 - let xdg = Xdg.create ~env:Sys.getenv_opt () in 468 - Filename.concat (Xdg.cache_dir xdg) app_name 469 - in 470 - 471 - let base_dir = Eio.Path.(Eio.Stdenv.fs env / cache_dir_path) in 472 - 473 - (* Ensure cache directory exists *) 474 - (try Eio.Path.mkdir ~perm:0o755 base_dir with _ -> ()); 475 - 476 - let storage = FileStorage.create ~base_dir ~app_name () in 477 - let cache = create ~storage:(`File storage) in 478 - 479 - (* Handle cache operations *) 480 - if config.cache_clear then begin 481 - clear cache; 482 - Logs.info (fun m -> m "Cache cleared") 483 - end; 484 - 485 - if config.cache_stats then begin 486 - let s = stats cache in 487 - let size_str = 488 - let open Int64 in 489 - if s.size < 1024L then 490 - Printf.sprintf "%Ld B" s.size 491 - else if s.size < mul 1024L 1024L then 492 - Printf.sprintf "%.1f KB" (to_float s.size /. 1024.0) 493 - else if s.size < mul (mul 1024L 1024L) 1024L then 494 - Printf.sprintf "%.1f MB" (to_float s.size /. 1024.0 /. 1024.0) 495 - else 496 - Printf.sprintf "%.1f GB" (to_float s.size /. 1024.0 /. 1024.0 /. 1024.0) 497 - in 498 - Logs.app (fun m -> m "Cache statistics:"); 499 - Logs.app (fun m -> m " Hits: %d" s.hits); 500 - Logs.app (fun m -> m " Misses: %d" s.misses); 501 - Logs.app (fun m -> m " Entries: %d" s.entries); 502 - Logs.app (fun m -> m " Size: %s" size_str) 503 - end; 504 - 505 - Some cache 506 - end 507 - 508 - (** {1 Utilities} *) 509 - 510 - let hours_to_ttl hours = 511 - float_of_int (hours * 3600) 512 - 513 - let is_expired ~created_at ~ttl = 514 - let now = Ptime_clock.now () in 515 - let age_span = Ptime.diff now created_at in 516 - let age_seconds = Ptime.Span.to_float_s age_span in 517 - age_seconds > ttl 518 - 519 - let format_size bytes = 520 - let open Int64 in 521 - if bytes < 1024L then 522 - Printf.sprintf "%Ld B" bytes 523 - else if bytes < mul 1024L 1024L then 524 - Printf.sprintf "%.1f KB" (to_float bytes /. 1024.0) 525 - else if bytes < mul (mul 1024L 1024L) 1024L then 526 - Printf.sprintf "%.1f MB" (to_float bytes /. 1024.0 /. 1024.0) 527 - else 528 - Printf.sprintf "%.1f GB" (to_float bytes /. 1024.0 /. 1024.0 /. 1024.0)
-175
cacheio/_build/default/lib/cacheio.mli
··· 1 - (** CacheIO - A flexible file-based caching library for OCaml with XDG support *) 2 - 3 - (** {1 Cache Storage} *) 4 - 5 - (** Cache statistics *) 6 - type stats = { 7 - hits : int; 8 - misses : int; 9 - size : int64; (** Total size in bytes *) 10 - entries : int; (** Number of cached entries *) 11 - } 12 - 13 - (** Cache storage backend *) 14 - module type STORAGE = sig 15 - type t 16 - 17 - val get : t -> key:string -> string option 18 - val put : t -> key:string -> data:string -> ttl:float -> unit 19 - val delete : t -> key:string -> unit 20 - val clear : t -> unit 21 - val exists : t -> key:string -> bool 22 - val stats : t -> stats 23 - val list_keys : t -> string list 24 - end 25 - 26 - (** {2 File-based Storage} *) 27 - 28 - module FileStorage : sig 29 - type t 30 - 31 - (** Create a file storage backend. 32 - @param base_dir The base directory for cache files 33 - @param max_size Maximum cache size in bytes (optional) 34 - @param app_name Application name for namespacing *) 35 - val create : 36 - base_dir:Eio.Fs.dir_ty Eio.Path.t -> 37 - ?max_size:int64 -> 38 - app_name:string -> 39 - unit -> t 40 - 41 - include STORAGE with type t := t 42 - 43 - (** Get the path to a cache entry *) 44 - val get_cache_path : t -> key:string -> Eio.Fs.dir_ty Eio.Path.t 45 - 46 - (** Cleanup expired entries *) 47 - val cleanup_expired : t -> int (** Returns number of entries removed *) 48 - end 49 - 50 - (** {2 Memory Storage} *) 51 - 52 - module MemoryStorage : sig 53 - type t 54 - 55 - (** Create an in-memory storage backend. 56 - @param max_entries Maximum number of entries (default: 1000) *) 57 - val create : ?max_entries:int -> unit -> t 58 - 59 - include STORAGE with type t := t 60 - end 61 - 62 - (** {1 Cache Interface} *) 63 - 64 - (** Main cache type *) 65 - type t 66 - 67 - (** Storage backend type *) 68 - type storage = [ 69 - | `File of FileStorage.t 70 - | `Memory of MemoryStorage.t 71 - ] 72 - 73 - (** Create a cache instance *) 74 - val create : storage:storage -> t 75 - 76 - (** {2 Core Operations} *) 77 - 78 - (** Get a value from the cache *) 79 - val get : t -> key:string -> string option 80 - 81 - (** Put a value into the cache with TTL in seconds *) 82 - val put : t -> key:string -> data:string -> ttl:float -> unit 83 - 84 - (** Delete a key from the cache *) 85 - val delete : t -> key:string -> unit 86 - 87 - (** Clear all cache entries *) 88 - val clear : t -> unit 89 - 90 - (** Check if a key exists and is not expired *) 91 - val exists : t -> key:string -> bool 92 - 93 - (** Get cache statistics *) 94 - val stats : t -> stats 95 - 96 - (** List all cache keys *) 97 - val list_keys : t -> string list 98 - 99 - (** {2 JSON Operations} *) 100 - 101 - (** Get and parse JSON from cache *) 102 - val get_json : t -> key:string -> Yojson.Safe.t option 103 - 104 - (** Store JSON in cache *) 105 - val put_json : t -> key:string -> data:Yojson.Safe.t -> ttl:float -> unit 106 - 107 - (** {2 Hierarchical Keys} *) 108 - 109 - (** Build a hierarchical cache key from path components *) 110 - val make_key : string list -> string 111 - 112 - (** Split a hierarchical key into components *) 113 - val split_key : string -> string list 114 - 115 - (** {1 XDG Support} *) 116 - 117 - (* XDG module removed - use the xdg library directly instead *) 118 - 119 - (** {1 Cmdliner Support} *) 120 - 121 - module Cmdliner : sig 122 - open Cmdliner 123 - 124 - (** Configuration for cache commands *) 125 - type config = { 126 - cache_dir : string option; 127 - no_cache : bool; 128 - cache_clear : bool; 129 - cache_stats : bool; 130 - cache_max_age : int option; (** Max age in hours *) 131 - } 132 - 133 - (** Default configuration *) 134 - val default_config : config 135 - 136 - (** Cmdliner term for cache directory *) 137 - val cache_dir : string option Term.t 138 - 139 - (** Cmdliner term for disabling cache *) 140 - val no_cache : bool Term.t 141 - 142 - (** Cmdliner term for clearing cache *) 143 - val cache_clear : bool Term.t 144 - 145 - (** Cmdliner term for showing cache stats *) 146 - val cache_stats : bool Term.t 147 - 148 - (** Cmdliner term for cache max age *) 149 - val cache_max_age : int option Term.t 150 - 151 - (** Combined term for all cache options *) 152 - val cache_config : config Term.t 153 - 154 - (** Process cache config and return a configured cache instance. 155 - Returns None if caching is disabled. 156 - @param env Eio environment 157 - @param app_name Application name for cache directory 158 - @param config Cache configuration from cmdliner *) 159 - val setup_cache : 160 - env:< fs : Eio.Fs.dir_ty Eio.Path.t ; .. > -> 161 - app_name:string -> 162 - config:config -> 163 - t option 164 - end 165 - 166 - (** {1 Utilities} *) 167 - 168 - (** Convert TTL in hours to seconds *) 169 - val hours_to_ttl : int -> float 170 - 171 - (** Check if a timestamp is expired given a TTL *) 172 - val is_expired : created_at:Ptime.t -> ttl:float -> bool 173 - 174 - (** Format cache size for display *) 175 - val format_size : int64 -> string
-6
cacheio/_build/default/xdg-eio/META.xdge
··· 1 - description = "" 2 - requires = "cmdliner eio eio_main fmt xdg" 3 - archive(byte) = "xdge.cma" 4 - archive(native) = "xdge.cmxa" 5 - plugin(byte) = "xdge.cma" 6 - plugin(native) = "xdge.cmxs"
cacheio/_build/default/xdg-eio/example/.merlin-conf/exe-xdg_example

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/example/.xdg_example.eobjs/byte/dune__exe__Xdg_example.cmi

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/example/.xdg_example.eobjs/byte/dune__exe__Xdg_example.cmo

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/example/.xdg_example.eobjs/byte/dune__exe__Xdg_example.cmt

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/example/.xdg_example.eobjs/byte/dune__exe__Xdg_example.cmti

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/example/.xdg_example.eobjs/native/dune__exe__Xdg_example.cmx

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/example/.xdg_example.eobjs/native/dune__exe__Xdg_example.o

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/example/xdg_example.exe

This is a binary file and will not be displayed.

-37
cacheio/_build/default/xdg-eio/example/xdg_example.ml
··· 1 - let run (xdg, cfg) = 2 - Fmt.pr 3 - "%a@.%a@.@.%a@.%a@." 4 - Fmt.(styled `Bold string) 5 - "=== Cmdliner Config ===" 6 - Xdge.Cmd.pp 7 - cfg 8 - Fmt.(styled `Bold string) 9 - "=== XDG Directories ===" 10 - (Xdge.pp ~brief:false ~sources:true) 11 - xdg 12 - ;; 13 - 14 - open Cmdliner 15 - 16 - let () = 17 - Fmt.set_style_renderer Fmt.stdout `Ansi_tty; 18 - let app_name = "xdg_example" in 19 - let doc = "Example program demonstrating XDG directory selection with Cmdliner" in 20 - let man = 21 - [ `S Manpage.s_description 22 - ; `P 23 - "This example shows how to use the Xdge library with Cmdliner to handle XDG Base \ 24 - Directory Specification paths with command-line and environment variable \ 25 - overrides." 26 - ; `S Manpage.s_environment 27 - ; `P (Xdge.Cmd.env_docs app_name) 28 - ] 29 - in 30 - let info = Cmdliner.Cmd.info "xdg_example" ~version:"1.0" ~doc ~man in 31 - Eio_main.run 32 - @@ fun env -> 33 - let create_xdg_term = Xdge.Cmd.term app_name env#fs in 34 - let main_term = Term.(const run $ create_xdg_term) in 35 - let cmd = Cmdliner.Cmd.v info main_term in 36 - exit @@ Cmdliner.Cmd.eval cmd 37 - ;;
-1
cacheio/_build/default/xdg-eio/example/xdg_example.mli
··· 1 - (* Auto-generated by Dune *)
cacheio/_build/default/xdg-eio/lib/.merlin-conf/lib-xdge

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/lib/.xdge.objs/byte/xdge.cmi

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/lib/.xdge.objs/byte/xdge.cmo

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/lib/.xdge.objs/byte/xdge.cmt

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/lib/.xdge.objs/byte/xdge.cmti

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/lib/.xdge.objs/native/xdge.cmx

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/lib/.xdge.objs/native/xdge.o

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/lib/xdge.a

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/lib/xdge.cma

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/lib/xdge.cmxa

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/lib/xdge.cmxs

This is a binary file and will not be displayed.

-712
cacheio/_build/default/xdg-eio/lib/xdge.ml
··· 1 - type source = 2 - | Default 3 - | Env of string 4 - | Cmdline 5 - 6 - type t = 7 - { app_name : string 8 - ; config_dir : Eio.Fs.dir_ty Eio.Path.t 9 - ; config_dir_source : source 10 - ; data_dir : Eio.Fs.dir_ty Eio.Path.t 11 - ; data_dir_source : source 12 - ; cache_dir : Eio.Fs.dir_ty Eio.Path.t 13 - ; cache_dir_source : source 14 - ; state_dir : Eio.Fs.dir_ty Eio.Path.t 15 - ; state_dir_source : source 16 - ; runtime_dir : Eio.Fs.dir_ty Eio.Path.t option 17 - ; runtime_dir_source : source 18 - ; config_dirs : Eio.Fs.dir_ty Eio.Path.t list 19 - ; data_dirs : Eio.Fs.dir_ty Eio.Path.t list 20 - } 21 - 22 - let ensure_dir ?(perm = 0o755) path = Eio.Path.mkdirs ~exists_ok:true ~perm path 23 - 24 - let validate_runtime_base_dir base_path = 25 - (* Validate the base XDG_RUNTIME_DIR has correct permissions per spec *) 26 - try 27 - let path_str = Eio.Path.native_exn base_path in 28 - let stat = Eio.Path.stat ~follow:true base_path in 29 - let current_perm = stat.perm land 0o777 in 30 - if current_perm <> 0o700 31 - then 32 - failwith 33 - (Printf.sprintf 34 - "XDG_RUNTIME_DIR base directory %s has incorrect permissions: %o (must be \ 35 - 0700)" 36 - path_str 37 - current_perm); 38 - (* Check ownership - directory should be owned by current user *) 39 - let uid = Unix.getuid () in 40 - if stat.uid <> Int64.of_int uid 41 - then 42 - failwith 43 - (Printf.sprintf 44 - "XDG_RUNTIME_DIR base directory %s not owned by current user (uid %d, owner \ 45 - %Ld)" 46 - path_str 47 - uid 48 - stat.uid) 49 - (* TODO: Check that directory is on local filesystem (not networked). 50 - This would require filesystem type detection which is OS-specific. *) 51 - with 52 - | exn -> 53 - failwith 54 - (Printf.sprintf "Cannot validate XDG_RUNTIME_DIR: %s" (Printexc.to_string exn)) 55 - ;; 56 - 57 - let ensure_runtime_dir _fs app_runtime_path = 58 - (* Base directory validation is done in resolve_runtime_dir, 59 - so we just create the app subdirectory *) 60 - ensure_dir app_runtime_path 61 - ;; 62 - 63 - let get_home_dir fs = 64 - let home_str = 65 - match Sys.getenv_opt "HOME" with 66 - | Some home -> home 67 - | None -> 68 - (match Sys.os_type with 69 - | "Win32" | "Cygwin" -> 70 - (match Sys.getenv_opt "USERPROFILE" with 71 - | Some profile -> profile 72 - | None -> failwith "Cannot determine home directory") 73 - | _ -> 74 - (try Unix.((getpwuid (getuid ())).pw_dir) with 75 - | _ -> failwith "Cannot determine home directory")) 76 - in 77 - Eio.Path.(fs / home_str) 78 - ;; 79 - 80 - let make_env_var_name app_name suffix = String.uppercase_ascii app_name ^ "_" ^ suffix 81 - 82 - exception Invalid_xdg_path of string 83 - 84 - let validate_absolute_path context path = 85 - if Filename.is_relative path 86 - then 87 - raise 88 - (Invalid_xdg_path 89 - (Printf.sprintf "%s must be an absolute path, got: %s" context path)) 90 - ;; 91 - 92 - let resolve_path fs home_path base_path = 93 - if Filename.is_relative base_path 94 - then Eio.Path.(home_path / base_path) 95 - else Eio.Path.(fs / base_path) 96 - ;; 97 - 98 - (* Helper to resolve system directories (config_dirs or data_dirs) *) 99 - let resolve_system_dirs fs home_path app_name override_suffix xdg_var default_paths = 100 - let override_var = make_env_var_name app_name override_suffix in 101 - match Sys.getenv_opt override_var with 102 - | Some dirs when dirs <> "" -> 103 - String.split_on_char ':' dirs 104 - |> List.filter (fun s -> s <> "") 105 - |> List.filter_map (fun path -> 106 - try 107 - validate_absolute_path override_var path; 108 - Some Eio.Path.(resolve_path fs home_path path / app_name) 109 - with 110 - | Invalid_xdg_path _ -> None) 111 - | Some _ | None -> 112 - (match Sys.getenv_opt xdg_var with 113 - | Some dirs when dirs <> "" -> 114 - String.split_on_char ':' dirs 115 - |> List.filter (fun s -> s <> "") 116 - |> List.filter_map (fun path -> 117 - try 118 - validate_absolute_path xdg_var path; 119 - Some Eio.Path.(resolve_path fs home_path path / app_name) 120 - with 121 - | Invalid_xdg_path _ -> None) 122 - | Some _ | None -> 123 - List.map 124 - (fun path -> Eio.Path.(resolve_path fs home_path path / app_name)) 125 - default_paths) 126 - ;; 127 - 128 - (* Helper to resolve a user directory with override precedence *) 129 - let resolve_user_dir fs home_path app_name xdg_ctx xdg_getter override_suffix = 130 - let override_var = make_env_var_name app_name override_suffix in 131 - match Sys.getenv_opt override_var with 132 - | Some dir when dir <> "" -> 133 - validate_absolute_path override_var dir; 134 - Eio.Path.(fs / dir / app_name), Env override_var 135 - | Some _ | None -> 136 - let xdg_base = xdg_getter xdg_ctx in 137 - let base_path = resolve_path fs home_path xdg_base in 138 - Eio.Path.(base_path / app_name), Default 139 - ;; 140 - 141 - (* Helper to resolve runtime directory (special case since it can be None) *) 142 - let resolve_runtime_dir fs home_path app_name xdg_ctx = 143 - let override_var = make_env_var_name app_name "RUNTIME_DIR" in 144 - match Sys.getenv_opt override_var with 145 - | Some dir when dir <> "" -> 146 - validate_absolute_path override_var dir; 147 - (* Validate the base runtime directory has correct permissions *) 148 - let base_runtime_dir = resolve_path fs home_path dir in 149 - validate_runtime_base_dir base_runtime_dir; 150 - Some Eio.Path.(base_runtime_dir / app_name), Env override_var 151 - | Some _ | None -> 152 - ( (match Xdg.runtime_dir xdg_ctx with 153 - | Some base -> 154 - (* Validate the base runtime directory has correct permissions *) 155 - let base_runtime_dir = resolve_path fs home_path base in 156 - validate_runtime_base_dir base_runtime_dir; 157 - Some Eio.Path.(base_runtime_dir / app_name) 158 - | None -> None) 159 - , Default ) 160 - ;; 161 - 162 - let validate_standard_xdg_vars () = 163 - (* Validate standard XDG environment variables for absolute paths *) 164 - let xdg_vars = 165 - [ "XDG_CONFIG_HOME" 166 - ; "XDG_DATA_HOME" 167 - ; "XDG_CACHE_HOME" 168 - ; "XDG_STATE_HOME" 169 - ; "XDG_RUNTIME_DIR" 170 - ; "XDG_CONFIG_DIRS" 171 - ; "XDG_DATA_DIRS" 172 - ] 173 - in 174 - List.iter 175 - (fun var -> 176 - match Sys.getenv_opt var with 177 - | Some value when value <> "" -> 178 - if String.contains value ':' 179 - then 180 - (* Colon-separated list - validate each part *) 181 - String.split_on_char ':' value 182 - |> List.filter (fun s -> s <> "") 183 - |> List.iter (fun path -> validate_absolute_path var path) 184 - else 185 - (* Single path *) 186 - validate_absolute_path var value 187 - | _ -> ()) 188 - xdg_vars 189 - ;; 190 - 191 - let create fs app_name = 192 - let fs = fs in 193 - let home_path = get_home_dir fs in 194 - (* First validate all standard XDG environment variables *) 195 - validate_standard_xdg_vars (); 196 - let xdg_ctx = Xdg.create ~env:Sys.getenv_opt () in 197 - (* User directories *) 198 - let config_dir, config_dir_source = 199 - resolve_user_dir fs home_path app_name xdg_ctx Xdg.config_dir "CONFIG_DIR" 200 - in 201 - let data_dir, data_dir_source = 202 - resolve_user_dir fs home_path app_name xdg_ctx Xdg.data_dir "DATA_DIR" 203 - in 204 - let cache_dir, cache_dir_source = 205 - resolve_user_dir fs home_path app_name xdg_ctx Xdg.cache_dir "CACHE_DIR" 206 - in 207 - let state_dir, state_dir_source = 208 - resolve_user_dir fs home_path app_name xdg_ctx Xdg.state_dir "STATE_DIR" 209 - in 210 - (* Runtime directory *) 211 - let runtime_dir, runtime_dir_source = 212 - resolve_runtime_dir fs home_path app_name xdg_ctx 213 - in 214 - (* System directories *) 215 - let config_dirs = 216 - resolve_system_dirs 217 - fs 218 - home_path 219 - app_name 220 - "CONFIG_DIRS" 221 - "XDG_CONFIG_DIRS" 222 - [ "/etc/xdg" ] 223 - in 224 - let data_dirs = 225 - resolve_system_dirs 226 - fs 227 - home_path 228 - app_name 229 - "DATA_DIRS" 230 - "XDG_DATA_DIRS" 231 - [ "/usr/local/share"; "/usr/share" ] 232 - in 233 - ensure_dir config_dir; 234 - ensure_dir data_dir; 235 - ensure_dir cache_dir; 236 - ensure_dir state_dir; 237 - Option.iter (ensure_runtime_dir fs) runtime_dir; 238 - { app_name 239 - ; config_dir 240 - ; config_dir_source 241 - ; data_dir 242 - ; data_dir_source 243 - ; cache_dir 244 - ; cache_dir_source 245 - ; state_dir 246 - ; state_dir_source 247 - ; runtime_dir 248 - ; runtime_dir_source 249 - ; config_dirs 250 - ; data_dirs 251 - } 252 - ;; 253 - 254 - let app_name t = t.app_name 255 - let config_dir t = t.config_dir 256 - let data_dir t = t.data_dir 257 - let cache_dir t = t.cache_dir 258 - let state_dir t = t.state_dir 259 - let runtime_dir t = t.runtime_dir 260 - let config_dirs t = t.config_dirs 261 - let data_dirs t = t.data_dirs 262 - 263 - (* File search following XDG specification *) 264 - let find_file_in_dirs dirs filename = 265 - let rec search_dirs = function 266 - | [] -> None 267 - | dir :: remaining_dirs -> 268 - let file_path = Eio.Path.(dir / filename) in 269 - (try 270 - (* Try to check if file exists and is readable *) 271 - let _ = Eio.Path.stat ~follow:true file_path in 272 - Some file_path 273 - with 274 - | _ -> 275 - (* File is inaccessible (non-existent, permissions, etc.) 276 - Skip and continue with next directory per XDG spec *) 277 - search_dirs remaining_dirs) 278 - in 279 - search_dirs dirs 280 - ;; 281 - 282 - let find_config_file t filename = 283 - (* Search user config dir first, then system config dirs *) 284 - find_file_in_dirs (t.config_dir :: t.config_dirs) filename 285 - ;; 286 - 287 - let find_data_file t filename = 288 - (* Search user data dir first, then system data dirs *) 289 - find_file_in_dirs (t.data_dir :: t.data_dirs) filename 290 - ;; 291 - 292 - let pp ?(brief = false) ?(sources = false) ppf t = 293 - let pp_source ppf = function 294 - | Default -> Fmt.(styled `Faint string) ppf "default" 295 - | Env var -> Fmt.pf ppf "%a" Fmt.(styled `Yellow string) ("env(" ^ var ^ ")") 296 - | Cmdline -> Fmt.(styled `Blue string) ppf "cmdline" 297 - in 298 - let pp_path_with_source ppf path source = 299 - if sources 300 - then 301 - Fmt.pf 302 - ppf 303 - "%a %a" 304 - Fmt.(styled `Green Eio.Path.pp) 305 - path 306 - Fmt.(styled `Faint (brackets pp_source)) 307 - source 308 - else Fmt.(styled `Green Eio.Path.pp) ppf path 309 - in 310 - let pp_path_opt_with_source ppf path_opt source = 311 - match path_opt with 312 - | None -> 313 - if sources 314 - then 315 - Fmt.pf 316 - ppf 317 - "%a %a" 318 - Fmt.(styled `Red string) 319 - "<none>" 320 - Fmt.(styled `Faint (brackets pp_source)) 321 - source 322 - else Fmt.(styled `Red string) ppf "<none>" 323 - | Some path -> pp_path_with_source ppf path source 324 - in 325 - let pp_paths ppf paths = 326 - Fmt.(list ~sep:(any ";@ ") (styled `Green Eio.Path.pp)) ppf paths 327 - in 328 - if brief 329 - then 330 - Fmt.pf 331 - ppf 332 - "%a config=%a data=%a>" 333 - Fmt.(styled `Cyan string) 334 - ("<xdg:" ^ t.app_name) 335 - (fun ppf (path, source) -> pp_path_with_source ppf path source) 336 - (t.config_dir, t.config_dir_source) 337 - (fun ppf (path, source) -> pp_path_with_source ppf path source) 338 - (t.data_dir, t.data_dir_source) 339 - else ( 340 - Fmt.pf 341 - ppf 342 - "@[<v>%a@," 343 - Fmt.(styled `Bold string) 344 - ("XDG directories for '" ^ t.app_name ^ "':"); 345 - Fmt.pf ppf "@[<v 2>%a@," Fmt.(styled `Bold string) "User directories:"; 346 - Fmt.pf 347 - ppf 348 - "%a %a@," 349 - Fmt.(styled `Cyan string) 350 - "config:" 351 - (fun ppf (path, source) -> pp_path_with_source ppf path source) 352 - (t.config_dir, t.config_dir_source); 353 - Fmt.pf 354 - ppf 355 - "%a %a@," 356 - Fmt.(styled `Cyan string) 357 - "data:" 358 - (fun ppf (path, source) -> pp_path_with_source ppf path source) 359 - (t.data_dir, t.data_dir_source); 360 - Fmt.pf 361 - ppf 362 - "%a %a@," 363 - Fmt.(styled `Cyan string) 364 - "cache:" 365 - (fun ppf (path, source) -> pp_path_with_source ppf path source) 366 - (t.cache_dir, t.cache_dir_source); 367 - Fmt.pf 368 - ppf 369 - "%a %a@," 370 - Fmt.(styled `Cyan string) 371 - "state:" 372 - (fun ppf (path, source) -> pp_path_with_source ppf path source) 373 - (t.state_dir, t.state_dir_source); 374 - Fmt.pf 375 - ppf 376 - "%a %a@]@," 377 - Fmt.(styled `Cyan string) 378 - "runtime:" 379 - (fun ppf (path_opt, source) -> pp_path_opt_with_source ppf path_opt source) 380 - (t.runtime_dir, t.runtime_dir_source); 381 - Fmt.pf ppf "@[<v 2>%a@," Fmt.(styled `Bold string) "System directories:"; 382 - Fmt.pf 383 - ppf 384 - "%a [@[<hov>%a@]]@," 385 - Fmt.(styled `Cyan string) 386 - "config_dirs:" 387 - pp_paths 388 - t.config_dirs; 389 - Fmt.pf 390 - ppf 391 - "%a [@[<hov>%a@]]@]@]" 392 - Fmt.(styled `Cyan string) 393 - "data_dirs:" 394 - pp_paths 395 - t.data_dirs) 396 - ;; 397 - 398 - module Cmd = struct 399 - type xdg_t = t 400 - 401 - type 'a with_source = 402 - { value : 'a option 403 - ; source : source 404 - } 405 - 406 - type t = 407 - { config_dir : string with_source 408 - ; data_dir : string with_source 409 - ; cache_dir : string with_source 410 - ; state_dir : string with_source 411 - ; runtime_dir : string with_source 412 - } 413 - 414 - let term app_name fs = 415 - let open Cmdliner in 416 - let app_upper = String.uppercase_ascii app_name in 417 - let show_paths = 418 - let doc = "Show only the resolved directory paths without formatting" in 419 - Arg.(value & flag & info [ "show-paths" ] ~doc) 420 - in 421 - let make_dir_arg name env_suffix xdg_var default_path = 422 - let app_env = app_upper ^ "_" ^ env_suffix in 423 - let doc = 424 - match default_path with 425 - | Some path -> 426 - Printf.sprintf 427 - "Override %s directory. Can also be set with %s or %s. Default: %s" 428 - name 429 - app_env 430 - xdg_var 431 - path 432 - | None -> 433 - Printf.sprintf 434 - "Override %s directory. Can also be set with %s or %s. No default value." 435 - name 436 - app_env 437 - xdg_var 438 - in 439 - let arg = 440 - Arg.(value & opt (some string) None & info [ name ^ "-dir" ] ~docv:"DIR" ~doc) 441 - in 442 - Term.( 443 - const (fun cmdline_val -> 444 - match cmdline_val with 445 - | Some v -> { value = Some v; source = Cmdline } 446 - | None -> 447 - (match Sys.getenv_opt app_env with 448 - | Some v when v <> "" -> { value = Some v; source = Env app_env } 449 - | Some _ | None -> 450 - (match Sys.getenv_opt xdg_var with 451 - | Some v -> { value = Some v; source = Env xdg_var } 452 - | None -> { value = None; source = Default }))) 453 - $ arg) 454 - in 455 - let home_prefix = "\\$HOME" in 456 - let config_dir = 457 - make_dir_arg 458 - "config" 459 - "CONFIG_DIR" 460 - "XDG_CONFIG_HOME" 461 - (Some (home_prefix ^ "/.config/" ^ app_name)) 462 - in 463 - let data_dir = 464 - make_dir_arg 465 - "data" 466 - "DATA_DIR" 467 - "XDG_DATA_HOME" 468 - (Some (home_prefix ^ "/.local/share/" ^ app_name)) 469 - in 470 - let cache_dir = 471 - make_dir_arg 472 - "cache" 473 - "CACHE_DIR" 474 - "XDG_CACHE_HOME" 475 - (Some (home_prefix ^ "/.cache/" ^ app_name)) 476 - in 477 - let state_dir = 478 - make_dir_arg 479 - "state" 480 - "STATE_DIR" 481 - "XDG_STATE_HOME" 482 - (Some (home_prefix ^ "/.local/state/" ^ app_name)) 483 - in 484 - let runtime_dir = make_dir_arg "runtime" "RUNTIME_DIR" "XDG_RUNTIME_DIR" None in 485 - Term.( 486 - const 487 - (fun 488 - show_paths_flag 489 - config_dir_ws 490 - data_dir_ws 491 - cache_dir_ws 492 - state_dir_ws 493 - runtime_dir_ws 494 - -> 495 - let config = 496 - { config_dir = config_dir_ws 497 - ; data_dir = data_dir_ws 498 - ; cache_dir = cache_dir_ws 499 - ; state_dir = state_dir_ws 500 - ; runtime_dir = runtime_dir_ws 501 - } 502 - in 503 - let home_path = get_home_dir fs in 504 - (* First validate all standard XDG environment variables *) 505 - validate_standard_xdg_vars (); 506 - let xdg_ctx = Xdg.create ~env:Sys.getenv_opt () in 507 - (* Helper to resolve directory from config with source tracking *) 508 - let resolve_from_config config_ws xdg_getter = 509 - match config_ws.value with 510 - | Some dir -> resolve_path fs home_path dir, config_ws.source 511 - | None -> 512 - let xdg_base = xdg_getter xdg_ctx in 513 - let base_path = resolve_path fs home_path xdg_base in 514 - Eio.Path.(base_path / app_name), config_ws.source 515 - in 516 - (* User directories *) 517 - let config_dir, config_dir_source = 518 - resolve_from_config config.config_dir Xdg.config_dir 519 - in 520 - let data_dir, data_dir_source = 521 - resolve_from_config config.data_dir Xdg.data_dir 522 - in 523 - let cache_dir, cache_dir_source = 524 - resolve_from_config config.cache_dir Xdg.cache_dir 525 - in 526 - let state_dir, state_dir_source = 527 - resolve_from_config config.state_dir Xdg.state_dir 528 - in 529 - (* Runtime directory *) 530 - let runtime_dir, runtime_dir_source = 531 - match config.runtime_dir.value with 532 - | Some dir -> Some (resolve_path fs home_path dir), config.runtime_dir.source 533 - | None -> 534 - ( Option.map 535 - (fun base -> 536 - let base_path = resolve_path fs home_path base in 537 - Eio.Path.(base_path / app_name)) 538 - (Xdg.runtime_dir xdg_ctx) 539 - , config.runtime_dir.source ) 540 - in 541 - (* System directories - reuse shared helper *) 542 - let config_dirs = 543 - resolve_system_dirs 544 - fs 545 - home_path 546 - app_name 547 - "CONFIG_DIRS" 548 - "XDG_CONFIG_DIRS" 549 - [ "/etc/xdg" ] 550 - in 551 - let data_dirs = 552 - resolve_system_dirs 553 - fs 554 - home_path 555 - app_name 556 - "DATA_DIRS" 557 - "XDG_DATA_DIRS" 558 - [ "/usr/local/share"; "/usr/share" ] 559 - in 560 - ensure_dir config_dir; 561 - ensure_dir data_dir; 562 - ensure_dir cache_dir; 563 - ensure_dir state_dir; 564 - Option.iter (ensure_runtime_dir fs) runtime_dir; 565 - let xdg = 566 - { app_name 567 - ; config_dir 568 - ; config_dir_source 569 - ; data_dir 570 - ; data_dir_source 571 - ; cache_dir 572 - ; cache_dir_source 573 - ; state_dir 574 - ; state_dir_source 575 - ; runtime_dir 576 - ; runtime_dir_source 577 - ; config_dirs 578 - ; data_dirs 579 - } 580 - in 581 - (* Handle --show-paths option *) 582 - if show_paths_flag 583 - then ( 584 - let print_path name path = 585 - match path with 586 - | None -> Printf.printf "%s: <none>\n" name 587 - | Some p -> Printf.printf "%s: %s\n" name (Eio.Path.native_exn p) 588 - in 589 - let print_paths name paths = 590 - match paths with 591 - | [] -> Printf.printf "%s: []\n" name 592 - | paths -> 593 - let paths_str = String.concat ":" (List.map Eio.Path.native_exn paths) in 594 - Printf.printf "%s: %s\n" name paths_str 595 - in 596 - print_path "config_dir" (Some config_dir); 597 - print_path "data_dir" (Some data_dir); 598 - print_path "cache_dir" (Some cache_dir); 599 - print_path "state_dir" (Some state_dir); 600 - print_path "runtime_dir" runtime_dir; 601 - print_paths "config_dirs" config_dirs; 602 - print_paths "data_dirs" data_dirs; 603 - Stdlib.exit 0); 604 - xdg, config) 605 - $ show_paths 606 - $ config_dir 607 - $ data_dir 608 - $ cache_dir 609 - $ state_dir 610 - $ runtime_dir) 611 - ;; 612 - 613 - let env_docs app_name = 614 - let app_upper = String.uppercase_ascii app_name in 615 - Printf.sprintf 616 - {| 617 - Configuration Precedence (follows standard Unix conventions): 618 - 1. Command-line flags (e.g., --config-dir) - highest priority 619 - 2. Application-specific environment variable (e.g., %s_CONFIG_DIR) 620 - 3. XDG standard environment variable (e.g., XDG_CONFIG_HOME) 621 - 4. Default path (e.g., ~/.config/%s) - lowest priority 622 - 623 - This allows per-application overrides without affecting other XDG-compliant programs. 624 - For example, setting %s_CONFIG_DIR only changes the config directory for %s, 625 - while XDG_CONFIG_HOME affects all XDG-compliant applications. 626 - 627 - Application-specific variables: 628 - %s_CONFIG_DIR Override config directory for %s only 629 - %s_DATA_DIR Override data directory for %s only 630 - %s_CACHE_DIR Override cache directory for %s only 631 - %s_STATE_DIR Override state directory for %s only 632 - %s_RUNTIME_DIR Override runtime directory for %s only 633 - 634 - XDG standard variables (shared by all XDG applications): 635 - XDG_CONFIG_HOME User configuration directory (default: ~/.config/%s) 636 - XDG_DATA_HOME User data directory (default: ~/.local/share/%s) 637 - XDG_CACHE_HOME User cache directory (default: ~/.cache/%s) 638 - XDG_STATE_HOME User state directory (default: ~/.local/state/%s) 639 - XDG_RUNTIME_DIR User runtime directory (no default) 640 - XDG_CONFIG_DIRS System configuration directories (default: /etc/xdg/%s) 641 - XDG_DATA_DIRS System data directories (default: /usr/local/share/%s:/usr/share/%s) 642 - |} 643 - app_upper 644 - app_name 645 - app_upper 646 - app_name 647 - app_upper 648 - app_name 649 - app_upper 650 - app_name 651 - app_upper 652 - app_name 653 - app_upper 654 - app_name 655 - app_upper 656 - app_name 657 - app_name 658 - app_name 659 - app_name 660 - app_name 661 - app_name 662 - app_name 663 - app_name 664 - ;; 665 - 666 - let pp ppf config = 667 - let pp_source ppf = function 668 - | Default -> Fmt.(styled `Faint string) ppf "default" 669 - | Env var -> Fmt.pf ppf "%a" Fmt.(styled `Yellow string) ("env(" ^ var ^ ")") 670 - | Cmdline -> Fmt.(styled `Blue string) ppf "cmdline" 671 - in 672 - let pp_with_source name ppf ws = 673 - match ws.value with 674 - | None when ws.source = Default -> () 675 - | None -> 676 - Fmt.pf 677 - ppf 678 - "@,%a %a %a" 679 - Fmt.(styled `Cyan string) 680 - (name ^ ":") 681 - Fmt.(styled `Red string) 682 - "<unset>" 683 - Fmt.(styled `Faint (brackets pp_source)) 684 - ws.source 685 - | Some value -> 686 - Fmt.pf 687 - ppf 688 - "@,%a %a %a" 689 - Fmt.(styled `Cyan string) 690 - (name ^ ":") 691 - Fmt.(styled `Green string) 692 - value 693 - Fmt.(styled `Faint (brackets pp_source)) 694 - ws.source 695 - in 696 - Fmt.pf 697 - ppf 698 - "@[<v>%a%a%a%a%a%a@]" 699 - Fmt.(styled `Bold string) 700 - "XDG config:" 701 - (pp_with_source "config_dir") 702 - config.config_dir 703 - (pp_with_source "data_dir") 704 - config.data_dir 705 - (pp_with_source "cache_dir") 706 - config.cache_dir 707 - (pp_with_source "state_dir") 708 - config.state_dir 709 - (pp_with_source "runtime_dir") 710 - config.runtime_dir 711 - ;; 712 - end
-433
cacheio/_build/default/xdg-eio/lib/xdge.mli
··· 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 Directory Specification} 10 - 11 - {b Key Concepts:} 12 - 13 - The XDG specification defines several types of directories: 14 - - {b User directories}: Store user-specific files (config, data, cache, state, runtime) 15 - - {b System directories}: Store system-wide files shared across users 16 - - {b Precedence}: User directories take precedence over system directories 17 - - {b Application isolation}: Each application gets its own subdirectory 18 - 19 - {b Environment Variable Precedence:} 20 - 21 - This library follows a three-level precedence system: 22 - + Application-specific variables (e.g., [MYAPP_CONFIG_DIR]) - highest priority 23 - + XDG standard variables (e.g., [XDG_CONFIG_HOME]) 24 - + Default paths (e.g., [$HOME/.config]) - lowest priority 25 - 26 - This allows fine-grained control over directory locations without affecting 27 - other XDG-compliant applications. 28 - 29 - {b Directory Creation:} 30 - 31 - All directories are automatically created with appropriate permissions (0o755) 32 - when accessed, except for runtime directories which require stricter permissions 33 - as per the specification. 34 - 35 - @see <https://specifications.freedesktop.org/basedir-spec/latest/> XDG Base Directory Specification *) 36 - 37 - (** The main XDG context type containing all directory paths for an application. 38 - 39 - A value of type [t] represents the complete XDG directory structure for a 40 - specific application, including both user-specific and system-wide directories. 41 - All paths are resolved at creation time and are absolute paths within the 42 - Eio filesystem. *) 43 - type t 44 - 45 - (** {1 Exceptions} *) 46 - 47 - (** Exception raised when XDG environment variables contain invalid paths. 48 - 49 - The XDG specification requires all paths in environment variables to be 50 - absolute. This exception is raised when a relative path is found. *) 51 - exception Invalid_xdg_path of string 52 - 53 - (** {1 Construction} *) 54 - 55 - (** [create fs app_name] creates an XDG context for the given application. 56 - 57 - This function initializes the complete XDG directory structure for your application, 58 - resolving all paths according to the environment variables and creating directories 59 - as needed. 60 - 61 - @param fs The Eio filesystem providing filesystem access 62 - @param app_name The name of your application (used as subdirectory name) 63 - 64 - {b Path Resolution:} 65 - 66 - For each directory type, the following precedence is used: 67 - + Application-specific environment variable (e.g., [MYAPP_CONFIG_DIR]) 68 - + XDG standard environment variable (e.g., [XDG_CONFIG_HOME]) 69 - + Default path as specified in the XDG specification 70 - 71 - {b Example:} 72 - {[ 73 - let xdg = Xdge.create env#fs "myapp" in 74 - let config = Xdge.config_dir xdg in 75 - (* config is now <fs:$HOME/.config/myapp> or the overridden path *) 76 - ]} 77 - 78 - {b Note:} All directories are created with permissions 0o755 if they don't exist, 79 - except for runtime directories which are created with 0o700 permissions and 80 - validated according to the XDG specification. 81 - 82 - @raise Invalid_xdg_path if any environment variable contains a relative path *) 83 - val create : Eio.Fs.dir_ty Eio.Path.t -> string -> t 84 - 85 - (** {1 Accessors} *) 86 - 87 - (** [app_name t] returns the application name used when creating this XDG context. 88 - 89 - This is the name that was passed to {!create} and is used as the subdirectory 90 - name within each XDG base directory. *) 91 - val app_name : t -> string 92 - 93 - (** {1 Base Directories} *) 94 - 95 - (** [config_dir t] returns the path to user-specific configuration files. 96 - 97 - {b Purpose:} Store user preferences, settings, and configuration files. 98 - Configuration files should be human-readable when possible. 99 - 100 - {b Environment Variables:} 101 - - [${APP_NAME}_CONFIG_DIR]: Application-specific override (highest priority) 102 - - [XDG_CONFIG_HOME]: XDG standard variable 103 - - Default: [$HOME/.config/{app_name}] 104 - 105 - {b Example Files:} 106 - - Application settings (e.g., [config.toml], [settings.json]) 107 - - User preferences 108 - - UI customizations 109 - 110 - @see <https://specifications.freedesktop.org/basedir-spec/latest/#variables> XDG_CONFIG_HOME specification *) 111 - val config_dir : t -> Eio.Fs.dir_ty Eio.Path.t 112 - 113 - (** [data_dir t] returns the path to user-specific data files. 114 - 115 - {b Purpose:} Store persistent application data that should be preserved 116 - across application restarts and system reboots. This data is typically 117 - not modified by users directly. 118 - 119 - {b Environment Variables:} 120 - - [${APP_NAME}_DATA_DIR]: Application-specific override (highest priority) 121 - - [XDG_DATA_HOME]: XDG standard variable 122 - - Default: [$HOME/.local/share/{app_name}] 123 - 124 - {b Example Files:} 125 - - Application databases 126 - - User-generated content (documents, projects) 127 - - Downloaded resources 128 - - Application plugins or extensions 129 - 130 - @see <https://specifications.freedesktop.org/basedir-spec/latest/#variables> XDG_DATA_HOME specification *) 131 - val data_dir : t -> Eio.Fs.dir_ty Eio.Path.t 132 - 133 - (** [cache_dir t] returns the path to user-specific cache files. 134 - 135 - {b Purpose:} Store non-essential cached data that can be regenerated 136 - if deleted. The application should remain functional if this directory 137 - is cleared, though performance may be temporarily impacted. 138 - 139 - {b Environment Variables:} 140 - - [${APP_NAME}_CACHE_DIR]: Application-specific override (highest priority) 141 - - [XDG_CACHE_HOME]: XDG standard variable 142 - - Default: [$HOME/.cache/{app_name}] 143 - 144 - {b Example Files:} 145 - - Downloaded thumbnails and previews 146 - - Compiled bytecode or object files 147 - - Network response caches 148 - - Temporary computation results 149 - 150 - {b Note:} Users may clear cache directories to free disk space, so 151 - always check for cache validity and be prepared to regenerate data. 152 - 153 - @see <https://specifications.freedesktop.org/basedir-spec/latest/#variables> XDG_CACHE_HOME specification *) 154 - val cache_dir : t -> Eio.Fs.dir_ty Eio.Path.t 155 - 156 - (** [state_dir t] returns the path to user-specific state files. 157 - 158 - {b Purpose:} Store persistent state data that should be preserved between 159 - application restarts but is not important enough to be user data. This 160 - includes application state that can be regenerated but would impact the 161 - user experience if lost. 162 - 163 - {b Environment Variables:} 164 - - [${APP_NAME}_STATE_DIR]: Application-specific override (highest priority) 165 - - [XDG_STATE_HOME]: XDG standard variable 166 - - Default: [$HOME/.local/state/{app_name}] 167 - 168 - {b Example Files:} 169 - - Application history (recently used files, command history) 170 - - Current application state (window positions, open tabs) 171 - - Logs and journal files 172 - - Undo/redo history 173 - 174 - {b Comparison with other directories:} 175 - - Unlike cache: State should persist between reboots 176 - - Unlike data: State can be regenerated (though inconvenient) 177 - - Unlike config: State changes frequently during normal use 178 - 179 - @see <https://specifications.freedesktop.org/basedir-spec/latest/#variables> XDG_STATE_HOME specification *) 180 - val state_dir : t -> Eio.Fs.dir_ty Eio.Path.t 181 - 182 - (** [runtime_dir t] returns the path to user-specific runtime files. 183 - 184 - {b Purpose:} Store runtime files such as sockets, named pipes, and 185 - process IDs. These files are only valid for the duration of the user's 186 - login session. 187 - 188 - {b Environment Variables:} 189 - - [${APP_NAME}_RUNTIME_DIR]: Application-specific override (highest priority) 190 - - [XDG_RUNTIME_DIR]: XDG standard variable 191 - - Default: None (returns [None] if not set) 192 - 193 - {b Required Properties (per specification):} 194 - - Owned by the user with access mode 0700 195 - - Bound to the user login session lifetime 196 - - Located on a local filesystem (not networked) 197 - - Fully-featured by the OS (supporting proper locking, etc.) 198 - 199 - {b Example Files:} 200 - - Unix domain sockets 201 - - Named pipes (FIFOs) 202 - - Lock files 203 - - Small process communication files 204 - 205 - {b Important:} This may return [None] if no suitable runtime directory 206 - is available. Applications should handle this gracefully, perhaps by 207 - falling back to [/tmp] with appropriate security measures. 208 - 209 - @see <https://specifications.freedesktop.org/basedir-spec/latest/#variables> XDG_RUNTIME_DIR specification *) 210 - val runtime_dir : t -> Eio.Fs.dir_ty Eio.Path.t option 211 - 212 - (** {1 System Directories} *) 213 - 214 - (** [config_dirs t] returns search paths for system-wide configuration files. 215 - 216 - {b Purpose:} Provide a search path for configuration files that are 217 - shared between multiple users. Files in user-specific {!config_dir} 218 - take precedence over these system directories. 219 - 220 - {b Environment Variables:} 221 - - [${APP_NAME}_CONFIG_DIRS]: Application-specific override (highest priority) 222 - - [XDG_CONFIG_DIRS]: XDG standard variable (colon-separated list) 223 - - Default: [[/etc/xdg/{app_name}]] 224 - 225 - {b Search Order:} 226 - Directories are ordered by preference, with earlier entries taking 227 - precedence over later ones. When looking for a configuration file, 228 - search {!config_dir} first, then each directory in this list. 229 - 230 - {b Example Usage:} 231 - - System-wide default configurations 232 - - Distribution-provided settings 233 - - Site-wide customizations 234 - 235 - @see <https://specifications.freedesktop.org/basedir-spec/latest/#variables> XDG_CONFIG_DIRS specification *) 236 - val config_dirs : t -> Eio.Fs.dir_ty Eio.Path.t list 237 - 238 - (** [data_dirs t] returns search paths for system-wide data files. 239 - 240 - {b Purpose:} Provide a search path for data files that are shared 241 - between multiple users. Files in user-specific {!data_dir} take 242 - precedence over these system directories. 243 - 244 - {b Environment Variables:} 245 - - [${APP_NAME}_DATA_DIRS]: Application-specific override (highest priority) 246 - - [XDG_DATA_DIRS]: XDG standard variable (colon-separated list) 247 - - Default: [[/usr/local/share/{app_name}; /usr/share/{app_name}]] 248 - 249 - {b Search Order:} 250 - Directories are ordered by preference, with earlier entries taking 251 - precedence over later ones. When looking for a data file, search 252 - {!data_dir} first, then each directory in this list. 253 - 254 - {b Example Files:} 255 - - Application icons and themes 256 - - Desktop files 257 - - Shared application resources 258 - - Documentation files 259 - - Default templates 260 - 261 - @see <https://specifications.freedesktop.org/basedir-spec/latest/#variables> XDG_DATA_DIRS specification *) 262 - val data_dirs : t -> Eio.Fs.dir_ty Eio.Path.t list 263 - 264 - (** {1 File Search} *) 265 - 266 - (** [find_config_file t filename] searches for a configuration file following XDG precedence. 267 - 268 - This function searches for the given filename in the user configuration directory 269 - first, then in system configuration directories in order of preference. 270 - Files that are inaccessible (due to permissions, non-existence, etc.) are 271 - silently skipped as per the XDG specification. 272 - 273 - @param t The XDG context 274 - @param filename The name of the file to search for 275 - @return [Some path] if found, [None] if not found in any directory 276 - 277 - {b Search Order:} 278 - 1. User config directory ({!config_dir}) 279 - 2. System config directories ({!config_dirs}) in preference order 280 - 281 - {b Example:} 282 - {[ 283 - match Xdge.find_config_file xdg "myapp.conf" with 284 - | Some path -> Printf.printf "Found config at: %s\n" (Eio.Path.native_exn path) 285 - | None -> Printf.printf "No config file found\n" 286 - ]} *) 287 - val find_config_file : t -> string -> Eio.Fs.dir_ty Eio.Path.t option 288 - 289 - (** [find_data_file t filename] searches for a data file following XDG precedence. 290 - 291 - This function searches for the given filename in the user data directory 292 - first, then in system data directories in order of preference. 293 - Files that are inaccessible (due to permissions, non-existence, etc.) are 294 - silently skipped as per the XDG specification. 295 - 296 - @param t The XDG context 297 - @param filename The name of the file to search for 298 - @return [Some path] if found, [None] if not found in any directory 299 - 300 - {b Search Order:} 301 - 1. User data directory ({!data_dir}) 302 - 2. System data directories ({!data_dirs}) in preference order 303 - 304 - {b Example:} 305 - {[ 306 - match Xdge.find_data_file xdg "templates/default.txt" with 307 - | Some path -> (* read from path *) 308 - | None -> (* use built-in default *) 309 - ]} *) 310 - val find_data_file : t -> string -> Eio.Fs.dir_ty Eio.Path.t option 311 - 312 - (** {1 Pretty Printing} *) 313 - 314 - (** [pp ?brief ?sources ppf t] pretty prints the XDG directory configuration. 315 - 316 - @param brief If [true], prints a compact one-line summary (default: [false]) 317 - @param sources If [true], shows the source of each directory value, 318 - indicating whether it came from defaults, environment 319 - variables, or command line (default: [false]) 320 - @param ppf The formatter to print to 321 - @param t The XDG context to print 322 - 323 - {b Output formats:} 324 - - Normal: Multi-line detailed view of all directories 325 - - Brief: Single line showing app name and key directories 326 - - With sources: Adds annotations showing where each path came from 327 - 328 - {b Example:} 329 - {[ 330 - (* Normal output *) 331 - Format.printf "%a" Xdge.pp xdg 332 - 333 - (* Brief output *) 334 - Format.printf "%a" (Xdge.pp ~brief:true) xdg 335 - 336 - (* Show sources *) 337 - Format.printf "%a" (Xdge.pp ~sources:true) xdg 338 - ]} *) 339 - val pp : ?brief:bool -> ?sources:bool -> Format.formatter -> t -> unit 340 - 341 - (** {1 Cmdliner Integration} *) 342 - 343 - module Cmd : sig 344 - (** The type of the outer XDG context *) 345 - type xdg_t = t 346 - (** Cmdliner integration for XDG directory configuration. 347 - 348 - This module provides seamless integration with the Cmdliner library, 349 - allowing XDG directories to be configured via command-line arguments 350 - while respecting the precedence of environment variables. 351 - 352 - {b Features:} 353 - - Automatic command-line flag generation for each directory type 354 - - Environment variable detection and precedence handling 355 - - Source tracking for debugging configuration issues 356 - - Pretty printing of configuration for --help output *) 357 - 358 - (** Complete XDG configuration gathered from command-line and environment. 359 - 360 - This contains all XDG directory paths along with their sources, 361 - as determined by command-line arguments and environment variables. *) 362 - type t 363 - 364 - (** [term app_name fs] creates a Cmdliner term for XDG directory configuration. 365 - 366 - This function generates a Cmdliner term that handles all XDG directory 367 - configuration through both command-line flags and environment variables, 368 - and directly returns the XDG context. 369 - 370 - @param app_name The application name (used for environment variable prefixes) 371 - @param fs The Eio filesystem to use for path resolution 372 - 373 - {b Generated Command-line Flags:} 374 - - [--config-dir DIR]: Override configuration directory 375 - - [--data-dir DIR]: Override data directory 376 - - [--cache-dir DIR]: Override cache directory 377 - - [--state-dir DIR]: Override state directory 378 - - [--runtime-dir DIR]: Override runtime directory 379 - 380 - {b Environment Variable Precedence:} 381 - For each directory type, the following precedence applies: 382 - + Command-line flag (e.g., [--config-dir]) 383 - + Application-specific variable (e.g., [MYAPP_CONFIG_DIR]) 384 - + XDG standard variable (e.g., [XDG_CONFIG_HOME]) 385 - + Default value 386 - 387 - {b Example:} 388 - {[ 389 - let open Cmdliner in 390 - let main (xdg, _config) = 391 - (* use xdg directly *) 392 - in 393 - let xdg_term = Cmd.term "myapp" env#fs in 394 - let main_term = Term.(const main $ xdg_term $ other_args) in 395 - (* ... *) 396 - ]} *) 397 - val term : string -> Eio.Fs.dir_ty Eio.Path.t -> (xdg_t * t) Cmdliner.Term.t 398 - 399 - (** [env_docs app_name] generates documentation for environment variables. 400 - 401 - Returns a formatted string documenting all environment variables that 402 - affect XDG directory configuration for the given application. This is 403 - useful for generating man pages or help text. 404 - 405 - @param app_name The application name 406 - @return A formatted documentation string 407 - 408 - {b Included Information:} 409 - - Configuration precedence rules 410 - - Application-specific environment variables 411 - - XDG standard environment variables 412 - - Default values for each directory type 413 - 414 - {b Example:} 415 - {[ 416 - let env_section = Cmdliner.Cmd.Env.info (env_docs "myapp") 417 - ]} *) 418 - val env_docs : string -> string 419 - 420 - (** [pp ppf config] pretty prints a Cmdliner configuration. 421 - 422 - This function formats the configuration showing each directory path 423 - along with its source, which is helpful for debugging configuration 424 - issues or displaying the current configuration to users. 425 - 426 - @param ppf The formatter to print to 427 - @param config The configuration to print 428 - 429 - {b Output Format:} 430 - Shows each directory with its path (if set) and source annotation 431 - in a color-coded format for easy reading. *) 432 - val pp : Format.formatter -> t -> unit 433 - end
cacheio/_build/default/xdg-eio/test/.merlin-conf/exe-test_paths

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/test/.test_paths.eobjs/byte/dune__exe__Test_paths.cmi

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/test/.test_paths.eobjs/byte/dune__exe__Test_paths.cmo

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/test/.test_paths.eobjs/byte/dune__exe__Test_paths.cmt

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/test/.test_paths.eobjs/byte/dune__exe__Test_paths.cmti

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/test/.test_paths.eobjs/native/dune__exe__Test_paths.cmx

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/test/.test_paths.eobjs/native/dune__exe__Test_paths.o

This is a binary file and will not be displayed.

cacheio/_build/default/xdg-eio/test/test_paths.exe

This is a binary file and will not be displayed.

-112
cacheio/_build/default/xdg-eio/test/test_paths.ml
··· 1 - let test_path_validation () = 2 - Printf.printf "Testing XDG path validation...\n"; 3 - (* Test absolute path validation for environment variables *) 4 - let test_relative_path_rejection env_var relative_path = 5 - Printf.printf "Testing rejection of relative path in %s...\n" env_var; 6 - Unix.putenv env_var relative_path; 7 - try 8 - Eio_main.run 9 - @@ fun env -> 10 - let _ = Xdge.create env#fs "test_validation" in 11 - Printf.printf "ERROR: Should have rejected relative path\n"; 12 - false 13 - with 14 - | Xdge.Invalid_xdg_path msg -> 15 - Printf.printf "SUCCESS: Correctly rejected relative path: %s\n" msg; 16 - true 17 - | exn -> 18 - Printf.printf "ERROR: Wrong exception: %s\n" (Printexc.to_string exn); 19 - false 20 - in 21 - let old_config_home = Sys.getenv_opt "XDG_CONFIG_HOME" in 22 - let old_data_dirs = Sys.getenv_opt "XDG_DATA_DIRS" in 23 - let success1 = test_relative_path_rejection "XDG_CONFIG_HOME" "relative/path" in 24 - let success2 = test_relative_path_rejection "XDG_DATA_DIRS" "rel1:rel2:/abs/path" in 25 - (* Restore original env vars *) 26 - (match old_config_home with 27 - | Some v -> Unix.putenv "XDG_CONFIG_HOME" v 28 - | None -> 29 - (try Unix.putenv "XDG_CONFIG_HOME" "" with 30 - | _ -> ())); 31 - (match old_data_dirs with 32 - | Some v -> Unix.putenv "XDG_DATA_DIRS" v 33 - | None -> 34 - (try Unix.putenv "XDG_DATA_DIRS" "" with 35 - | _ -> ())); 36 - success1 && success2 37 - ;; 38 - 39 - let test_file_search () = 40 - Printf.printf "\nTesting XDG file search...\n"; 41 - Eio_main.run 42 - @@ fun env -> 43 - let xdg = Xdge.create env#fs "search_test" in 44 - (* Create test files *) 45 - let config_file = Eio.Path.(Xdge.config_dir xdg / "test.conf") in 46 - let data_file = Eio.Path.(Xdge.data_dir xdg / "test.dat") in 47 - Eio.Path.save ~create:(`Or_truncate 0o644) config_file "config content"; 48 - Eio.Path.save ~create:(`Or_truncate 0o644) data_file "data content"; 49 - (* Test finding existing files *) 50 - (match Xdge.find_config_file xdg "test.conf" with 51 - | Some path -> 52 - let content = Eio.Path.load path in 53 - Printf.printf "Found config file: %s\n" (String.trim content) 54 - | None -> Printf.printf "ERROR: Config file not found\n"); 55 - (match Xdge.find_data_file xdg "test.dat" with 56 - | Some path -> 57 - let content = Eio.Path.load path in 58 - Printf.printf "Found data file: %s\n" (String.trim content) 59 - | None -> Printf.printf "ERROR: Data file not found\n"); 60 - (* Test non-existent file *) 61 - match Xdge.find_config_file xdg "nonexistent.conf" with 62 - | Some _ -> Printf.printf "ERROR: Should not have found nonexistent file\n" 63 - | None -> Printf.printf "Correctly handled nonexistent file\n" 64 - ;; 65 - 66 - let () = 67 - (* Check if we should run validation tests *) 68 - if Array.length Sys.argv > 1 && Sys.argv.(1) = "--validate" 69 - then ( 70 - let validation_success = test_path_validation () in 71 - test_file_search (); 72 - if validation_success 73 - then Printf.printf "\nAll path validation tests passed!\n" 74 - else Printf.printf "\nSome validation tests failed!\n") 75 - else 76 - (* Run original simple functionality test *) 77 - Eio_main.run 78 - @@ fun env -> 79 - let xdg = Xdge.create env#fs "path_test" in 80 - (* Test config subdirectory *) 81 - let profiles_path = Eio.Path.(Xdge.config_dir xdg / "profiles") in 82 - let profile_file = Eio.Path.(profiles_path / "default.json") in 83 - (try 84 - let content = Eio.Path.load profile_file in 85 - Printf.printf "config file content: %s" (String.trim content) 86 - with 87 - | exn -> Printf.printf "config file error: %s" (Printexc.to_string exn)); 88 - (* Test data subdirectory *) 89 - let db_path = Eio.Path.(Xdge.data_dir xdg / "databases") in 90 - let db_file = Eio.Path.(db_path / "main.db") in 91 - (try 92 - let content = Eio.Path.load db_file in 93 - Printf.printf "\ndata file content: %s" (String.trim content) 94 - with 95 - | exn -> Printf.printf "\ndata file error: %s" (Printexc.to_string exn)); 96 - (* Test cache subdirectory *) 97 - let cache_path = Eio.Path.(Xdge.cache_dir xdg / "thumbnails") in 98 - let cache_file = Eio.Path.(cache_path / "thumb1.png") in 99 - (try 100 - let content = Eio.Path.load cache_file in 101 - Printf.printf "\ncache file content: %s" (String.trim content) 102 - with 103 - | exn -> Printf.printf "\ncache file error: %s" (Printexc.to_string exn)); 104 - (* Test state subdirectory *) 105 - let logs_path = Eio.Path.(Xdge.state_dir xdg / "logs") in 106 - let log_file = Eio.Path.(logs_path / "app.log") in 107 - try 108 - let content = Eio.Path.load log_file in 109 - Printf.printf "\nstate file content: %s\n" (String.trim content) 110 - with 111 - | exn -> Printf.printf "\nstate file error: %s\n" (Printexc.to_string exn) 112 - ;;
-1
cacheio/_build/default/xdg-eio/test/test_paths.mli
··· 1 - (* Auto-generated by Dune *)
-33
cacheio/_build/default/xdg-eio/xdge.dune-package
··· 1 - (lang dune 3.20) 2 - (name xdge) 3 - (sections (lib .) (libexec .) (bin ../../bin)) 4 - (files 5 - (lib 6 - (META 7 - dune-package 8 - opam 9 - xdge.a 10 - xdge.cma 11 - xdge.cmi 12 - xdge.cmt 13 - xdge.cmti 14 - xdge.cmx 15 - xdge.cmxa 16 - xdge.ml 17 - xdge.mli)) 18 - (libexec (xdge.cmxs)) 19 - (bin (xdg_example))) 20 - (library 21 - (name xdge) 22 - (kind normal) 23 - (archives (byte xdge.cma) (native xdge.cmxa)) 24 - (plugins (byte xdge.cma) (native xdge.cmxs)) 25 - (native_archives xdge.a) 26 - (requires eio eio_main xdg cmdliner fmt) 27 - (main_module_name Xdge) 28 - (modes byte native) 29 - (modules 30 - (singleton 31 - (obj_name xdge) 32 - (visibility public) 33 - (source (path Xdge) (intf (path xdge.mli)) (impl (path xdge.ml))))))
-20
cacheio/_build/default/xdg-eio/xdge.install
··· 1 - lib: [ 2 - "_build/install/default/lib/xdge/META" 3 - "_build/install/default/lib/xdge/dune-package" 4 - "_build/install/default/lib/xdge/opam" 5 - "_build/install/default/lib/xdge/xdge.a" 6 - "_build/install/default/lib/xdge/xdge.cma" 7 - "_build/install/default/lib/xdge/xdge.cmi" 8 - "_build/install/default/lib/xdge/xdge.cmt" 9 - "_build/install/default/lib/xdge/xdge.cmti" 10 - "_build/install/default/lib/xdge/xdge.cmx" 11 - "_build/install/default/lib/xdge/xdge.cmxa" 12 - "_build/install/default/lib/xdge/xdge.ml" 13 - "_build/install/default/lib/xdge/xdge.mli" 14 - ] 15 - libexec: [ 16 - "_build/install/default/lib/xdge/xdge.cmxs" 17 - ] 18 - bin: [ 19 - "_build/install/default/bin/xdg_example" 20 - ]
-36
cacheio/_build/default/xdg-eio/xdge.opam
··· 1 - # This file is generated by dune, edit dune-project instead 2 - opam-version: "2.0" 3 - synopsis: "XDG Base Directory Specification support for Eio" 4 - description: 5 - "This library implements the XDG Base Directory Specification with Eio capabilities to provides safe access to configuration, data, cache, state, and runtime directories with proper environment variable overrides and Cmdliner integration." 6 - maintainer: ["Anil Madhavapeddy <anil@recoil.org>"] 7 - authors: ["Anil Madhavapeddy"] 8 - license: "ISC" 9 - homepage: "https://tangled.sh/@anil.recoil.org/ocaml-gpx" 10 - bug-reports: "https://tangled.sh/@anil.recoil.org/xgde" 11 - depends: [ 12 - "dune" {>= "3.20"} 13 - "ocaml" {>= "5.1.0"} 14 - "eio" {>= "1.1"} 15 - "eio_main" 16 - "xdg" {>= "3.9.0"} 17 - "cmdliner" {>= "1.2.0"} 18 - "fmt" {>= "0.11.0"} 19 - "odoc" {with-doc} 20 - "alcotest" {with-test & >= "1.7.0"} 21 - ] 22 - build: [ 23 - ["dune" "subst"] {dev} 24 - [ 25 - "dune" 26 - "build" 27 - "-p" 28 - name 29 - "-j" 30 - jobs 31 - "@install" 32 - "@runtest" {with-test} 33 - "@doc" {with-doc} 34 - ] 35 - ] 36 - x-maintenance-intent: ["(latest)"]
-1
cacheio/_build/install/default/bin/cacheio-example
··· 1 - ../../../default/bin/example.exe
-1
cacheio/_build/install/default/bin/xdg_example
··· 1 - ../../../default/xdg-eio/example/xdg_example.exe
-1
cacheio/_build/install/default/doc/cacheio/README.md
··· 1 - ../../../../default/README.md
-1
cacheio/_build/install/default/lib/cacheio/META
··· 1 - ../../../../default/META.cacheio
-1
cacheio/_build/install/default/lib/cacheio/cacheio.a
··· 1 - ../../../../default/lib/cacheio.a
-1
cacheio/_build/install/default/lib/cacheio/cacheio.cma
··· 1 - ../../../../default/lib/cacheio.cma
-1
cacheio/_build/install/default/lib/cacheio/cacheio.cmi
··· 1 - ../../../../default/lib/.cacheio.objs/byte/cacheio.cmi
-1
cacheio/_build/install/default/lib/cacheio/cacheio.cmt
··· 1 - ../../../../default/lib/.cacheio.objs/byte/cacheio.cmt
-1
cacheio/_build/install/default/lib/cacheio/cacheio.cmti
··· 1 - ../../../../default/lib/.cacheio.objs/byte/cacheio.cmti
-1
cacheio/_build/install/default/lib/cacheio/cacheio.cmx
··· 1 - ../../../../default/lib/.cacheio.objs/native/cacheio.cmx
-1
cacheio/_build/install/default/lib/cacheio/cacheio.cmxa
··· 1 - ../../../../default/lib/cacheio.cmxa
-1
cacheio/_build/install/default/lib/cacheio/cacheio.cmxs
··· 1 - ../../../../default/lib/cacheio.cmxs
-1
cacheio/_build/install/default/lib/cacheio/cacheio.ml
··· 1 - ../../../../default/lib/cacheio.ml
-1
cacheio/_build/install/default/lib/cacheio/cacheio.mli
··· 1 - ../../../../default/lib/cacheio.mli
-1
cacheio/_build/install/default/lib/cacheio/dune-package
··· 1 - ../../../../default/cacheio.dune-package
-1
cacheio/_build/install/default/lib/cacheio/opam
··· 1 - ../../../../default/cacheio.opam
-1
cacheio/_build/install/default/lib/xdge/META
··· 1 - ../../../../default/xdg-eio/META.xdge
-1
cacheio/_build/install/default/lib/xdge/dune-package
··· 1 - ../../../../default/xdg-eio/xdge.dune-package
-1
cacheio/_build/install/default/lib/xdge/opam
··· 1 - ../../../../default/xdg-eio/xdge.opam
-1
cacheio/_build/install/default/lib/xdge/xdge.a
··· 1 - ../../../../default/xdg-eio/lib/xdge.a
-1
cacheio/_build/install/default/lib/xdge/xdge.cma
··· 1 - ../../../../default/xdg-eio/lib/xdge.cma
-1
cacheio/_build/install/default/lib/xdge/xdge.cmi
··· 1 - ../../../../default/xdg-eio/lib/.xdge.objs/byte/xdge.cmi
-1
cacheio/_build/install/default/lib/xdge/xdge.cmt
··· 1 - ../../../../default/xdg-eio/lib/.xdge.objs/byte/xdge.cmt
-1
cacheio/_build/install/default/lib/xdge/xdge.cmti
··· 1 - ../../../../default/xdg-eio/lib/.xdge.objs/byte/xdge.cmti
-1
cacheio/_build/install/default/lib/xdge/xdge.cmx
··· 1 - ../../../../default/xdg-eio/lib/.xdge.objs/native/xdge.cmx
-1
cacheio/_build/install/default/lib/xdge/xdge.cmxa
··· 1 - ../../../../default/xdg-eio/lib/xdge.cmxa
-1
cacheio/_build/install/default/lib/xdge/xdge.cmxs
··· 1 - ../../../../default/xdg-eio/lib/xdge.cmxs
-1
cacheio/_build/install/default/lib/xdge/xdge.ml
··· 1 - ../../../../default/xdg-eio/lib/xdge.ml
-1
cacheio/_build/install/default/lib/xdge/xdge.mli
··· 1 - ../../../../default/xdg-eio/lib/xdge.mli
-23
cacheio/_build/log
··· 1 - # dune build @check 2 - # OCAMLPARAM: unset 3 - # Shared cache: enabled-except-user-rules 4 - # Shared cache location: /Users/avsm/.cache/dune/db 5 - # Workspace root: /Users/avsm/src/git/knot/slop/cacheio 6 - # Auto-detected concurrency: 16 7 - # Dune context: 8 - # { name = "default" 9 - # ; kind = "default" 10 - # ; profile = Dev 11 - # ; merlin = true 12 - # ; fdo_target_exe = None 13 - # ; build_dir = In_build_dir "default" 14 - # ; instrument_with = [] 15 - # } 16 - $ /Users/avsm/.opam/5.3.0/bin/ocamlc.opt -config > /var/folders/sg/gylc81bj6s54lsgs0zwh3qxc0000gn/T/dune_bcc573_output 17 - $ (cd _build/default && /Users/avsm/.opam/5.3.0/bin/ocamlc.opt -w @1..3@5..28@31..39@43@46..47@49..57@61..62@67@69-40 -strict-sequence -strict-formats -short-paths -keep-locs -g -bin-annot -bin-annot-occurrences -I lib/.cacheio.objs/byte -I /Users/avsm/.opam/5.3.0/lib/bigstringaf -I /Users/avsm/.opam/5.3.0/lib/cmdliner -I /Users/avsm/.opam/5.3.0/lib/cstruct -I /Users/avsm/.opam/5.3.0/lib/eio -I /Users/avsm/.opam/5.3.0/lib/eio/core -I /Users/avsm/.opam/5.3.0/lib/eio/runtime_events -I /Users/avsm/.opam/5.3.0/lib/fmt -I /Users/avsm/.opam/5.3.0/lib/hmap -I /Users/avsm/.opam/5.3.0/lib/logs -I /Users/avsm/.opam/5.3.0/lib/lwt-dllist -I /Users/avsm/.opam/5.3.0/lib/mtime -I /Users/avsm/.opam/5.3.0/lib/ocaml/runtime_events -I /Users/avsm/.opam/5.3.0/lib/optint -I /Users/avsm/.opam/5.3.0/lib/ptime -I /Users/avsm/.opam/5.3.0/lib/ptime/clock -I /Users/avsm/.opam/5.3.0/lib/seq -I /Users/avsm/.opam/5.3.0/lib/xdg -I /Users/avsm/.opam/5.3.0/lib/yojson -cmi-file lib/.cacheio.objs/byte/cacheio.cmi -no-alias-deps -opaque -o lib/.cacheio.objs/byte/cacheio.cmo -c -impl lib/cacheio.ml) 18 - > File "lib/cacheio.ml", line 30, characters 4-28: 19 - > 30 | max_size : int64 option; 20 - > ^^^^^^^^^^^^^^^^^^^^^^^^ 21 - > Error (warning 69 [unused-field]): record field max_size is never read. 22 - > (However, this field is used to build or mutate values.) 23 - [2]