My agentic slop goes here. Not intended for anyone else!

CacheIO#

A flexible file-based caching library for OCaml with XDG Base Directory support, TTL-based expiration, and built-in cmdliner integration.

Features#

  • File-based and Memory Storage: Choose between persistent file storage or fast in-memory caching
  • XDG Compliance: Follows XDG Base Directory specification for cache locations
  • TTL-based Expiration: Automatic expiration of cached entries
  • JSON Support: Built-in JSON serialization/deserialization
  • Hierarchical Keys: Support for path-like cache keys
  • Cmdliner Integration: Ready-to-use command-line options for cache management
  • Thread-safe: Safe concurrent access using Eio mutexes
  • Statistics: Track cache hits, misses, size, and entry count

Installation#

opam install cacheio

Or add to your dune-project:

(package
 (name myapp)
 (depends
  cacheio
  ...))

Basic Usage#

open Cacheio

let main () =
  Eio_main.run @@ fun env ->

  (* Create file-based cache *)
  let base_dir = Eio.Path.(Eio.Stdenv.fs env / "/tmp/cache") in
  let storage = FileStorage.create ~base_dir ~app_name:"myapp" () in
  let cache = create ~storage:(`File storage) in

  (* Store and retrieve data *)
  put cache ~key:"user:123" ~data:"John Doe" ~ttl:3600.0;

  match get cache ~key:"user:123" with
  | Some data -> Printf.printf "Found: %s\n" data
  | None -> Printf.printf "Not found or expired\n"

JSON Support#

(* Store JSON data *)
let user_data = `Assoc [
  ("id", `Int 123);
  ("name", `String "John Doe");
  ("email", `String "john@example.com")
] in

put_json cache ~key:"user:123" ~data:user_data ~ttl:3600.0;

(* Retrieve and parse JSON *)
match get_json cache ~key:"user:123" with
| Some json -> (* Handle JSON data *)
| None -> (* Handle cache miss *)

Hierarchical Keys#

(* Build structured cache keys *)
let key = make_key ["github"; "ocaml"; "ocaml"; "issues"; "1234"] in
(* Results in: "github/ocaml/ocaml/issues/1234" *)

(* Split keys back into components *)
let components = split_key key in
(* Returns: ["github"; "ocaml"; "ocaml"; "issues"; "1234"] *)

Cmdliner Integration#

Add cache management options to your CLI application:

open Cmdliner

let main cache_config =
  Eio_main.run @@ fun env ->

  (* Setup cache with CLI configuration *)
  match Cacheio.Cmdliner.setup_cache ~env ~app_name:"myapp" ~config:cache_config with
  | None -> print_endline "Caching disabled"
  | Some cache ->
      (* Use cache in your application *)
      ...

let cmd =
  let doc = "My application with caching" in
  let info = Cmd.info "myapp" ~doc in
  Cmd.v info Term.(const main $ Cacheio.Cmdliner.cache_config)

let () = exit (Cmd.eval cmd)

This provides the following CLI options:

  • --cache-dir DIR: Override cache directory
  • --no-cache: Disable caching
  • --cache-clear: Clear cache before running
  • --cache-stats: Show cache statistics
  • --cache-max-age HOURS: Set maximum cache age

XDG Support#

The library follows XDG Base Directory specification:

(* Get standard directories using the xdg library *)
let xdg = Xdg.create ~env:Sys.getenv_opt () in
let cache_home = Xdg.cache_dir xdg in
(* Returns: $XDG_CACHE_HOME or ~/.cache *)

let app_cache = Filename.concat (Xdg.cache_dir xdg) "myapp" in
(* Returns: $XDG_CACHE_HOME/myapp or ~/.cache/myapp *)

Memory Storage#

For temporary caching without disk persistence:

let storage = MemoryStorage.create ~max_entries:1000 () in
let cache = create ~storage:(`Memory storage) in

(* Use same interface as file storage *)
put cache ~key:"temp" ~data:"value" ~ttl:60.0

Statistics#

Monitor cache performance:

let stats = stats cache in
Printf.printf "Cache hits: %d\n" stats.hits;
Printf.printf "Cache misses: %d\n" stats.misses;
Printf.printf "Total size: %s\n" (format_size stats.size);
Printf.printf "Entries: %d\n" stats.entries

Integration with Requests Library#

The cacheio library can be used with the requests library for HTTP caching:

(* In requests library *)
module Cache = struct
  type 'a t = Cacheio.t

  let create ~max_size:_ () =
    let storage = Cacheio.MemoryStorage.create ~max_entries:max_size () in
    Cacheio.create ~storage:(`Memory storage)

  let get t ~method_ ~url ~headers:_ =
    if method_ = `GET then
      match Cacheio.get t ~key:(Uri.to_string url) with
      | Some data ->
          (* Deserialize response *)
          Some (deserialize_response data)
      | None -> None
    else None

  let put t ~method_ ~url ~response =
    if method_ = `GET && Response.is_success response then
      let data = serialize_response response in
      Cacheio.put t ~key:(Uri.to_string url) ~data ~ttl:3600.0
end

License#

MIT