(** File-backed persistent cache with streaming API and TTL support Cacheio provides a robust file-backed caching system designed for high-performance applications that need persistent storage with automatic expiry management. {2 Key Features} - {b SHA256-based key hashing}: Uniform distribution across subdirectories - {b Maildir++-style naming}: Atomic operations with flag and TTL encoding - {b Streaming API}: Built on Eio.Flow for efficient, non-blocking I/O - {b Automatic expiry}: TTL-based cache expiration with manual control - {b Thread-safe}: All operations are safe for concurrent access - {b Chunk support}: Resume partial downloads with range-based chunks - {b XDG integration}: Automatic directory management via XDG Base Directory spec {2 Usage Example} {[ let cache = Cacheio.create ~base_dir:cache_dir in (* Store data with 1 hour TTL *) Eio.Switch.run @@ fun sw -> let source = Eio.Flow.string_source "Hello, World!" in Cacheio.put cache ~key:"greeting" ~source ~ttl:(Some 3600.0) (); (* Retrieve data *) Eio.Switch.run @@ fun sw -> match Cacheio.get cache ~key:"greeting" ~sw with | Some source -> let content = Eio.Buf_read.(parse_exn take_all) source ~max_size:Int.max_int in print_endline content | None -> print_endline "Not found" ]}*) (** {1 Core Operations} *) (** Abstract type representing a cache instance *) type t (** Create a new cache at the specified base directory *) val create : base_dir:Eio.Fs.dir_ty Eio.Path.t -> t (** Create a new cache using an Xdge context *) val create_with_xdge : Xdge.t -> t (** Check if a key exists in the cache. Keys are strings that will be automatically hashed for filesystem storage. *) val exists : t -> key:string -> bool (** Get a stream source for reading cached data *) val get : t -> key:string -> sw:Eio.Switch.t -> Eio.Flow.source_ty Eio.Resource.t option (** Cache flags management *) module Flags = Flags (** Store data from a stream source with optional TTL and flags *) val put : t -> key:string -> source:Eio.Flow.source_ty Eio.Resource.t -> ?ttl:float option -> ?flags:Flags.t -> unit -> unit (** Delete a cache entry *) val delete : t -> key:string -> unit (** Get the size of a cache entry in bytes *) val size : t -> key:string -> int64 option (** Get the path to a cache entry (for direct file access if needed) *) val get_path : t -> key:string -> Eio.Fs.dir_ty Eio.Path.t option (** {1 Flag Management} *) (** Add a flag to an entry *) val add_flag : t -> key:string -> Flags.flag -> unit (** Remove a flag from an entry *) val remove_flag : t -> key:string -> Flags.flag -> unit (** Set specific flags on an entry (replaces all existing) *) val set_flags : t -> key:string -> Flags.t -> unit (** Get current flags for an entry *) val get_flags : t -> key:string -> Flags.t option (** {1 Cache Management} *) (** Cache entry information *) module Entry = Entry (** Cache statistics *) module Stats = Stats (** Scan the cache and return all entries *) val scan : t -> Entry.t list (** Get cache statistics *) val stats : t -> Stats.t (** Remove expired entries and return count of removed entries *) val expire : t -> int (** Clear all entries (except pinned ones) *) val clear : t -> unit (** Clear all temporary entries *) val clear_temporary : t -> unit (** {1 Chunk Support for Partial Downloads} *) (** Range management for partial downloads *) module Range = Range (** Chunk file management *) module Chunk = Chunk (** Store a chunk of data with a specific byte range *) val put_chunk : t -> key:string -> range:Range.t -> source:Eio.Flow.source_ty Eio.Resource.t -> ?ttl:float option -> ?flags:Flags.t -> unit -> unit (** Get data for a specific range, returns either complete file or chunks *) val get_range : t -> key:string -> range:Range.t -> sw:Eio.Switch.t -> [ `Complete of Eio.Flow.source_ty Eio.Resource.t | `Chunks of (Range.t * Eio.Flow.source_ty Eio.Resource.t) list | `Not_found ] (** List all chunks for a key *) val list_chunks : t -> key:string -> Chunk.t list (** Check if chunks form a complete sequence *) val has_complete_chunks : t -> key:string -> total_size:int64 -> bool (** Get missing byte ranges for incomplete downloads *) val missing_ranges : t -> key:string -> total_size:int64 -> Range.t list (** Coalesce chunks into a complete file asynchronously *) val coalesce_chunks : t -> key:string -> ?verify:bool -> ?flags:Flags.t -> ?ttl:float option -> unit -> unit Eio.Promise.or_exn (** Clean up all chunks for a key *) val cleanup_chunks : t -> key:string -> unit (** {1 Pretty Printing} *) (** Pretty printer for cache *) val pp : Format.formatter -> t -> unit