My agentic slop goes here. Not intended for anyone else!
1(** Command-line interface for cache management *) 2 3open Cmdliner 4open Eio 5 6let list_cmd ~app_name fs _stdin = 7 let list cache_dir stale expired pinned temporary older_than = 8 let base_dir = Path.(fs / cache_dir) in 9 let cache = Cacheio.create ~base_dir in 10 let entries = Cacheio.scan cache in 11 12 (* Filter entries based on flags *) 13 let filtered = List.filter (fun e -> 14 let open Cacheio.Entry in 15 (not stale || is_stale e) && 16 (not expired || is_expired e) && 17 (not pinned || is_pinned e) && 18 (not temporary || is_temporary e) && 19 (match older_than with 20 | None -> true 21 | Some days -> 22 let age = (Unix.time () -. mtime e) /. 86400. in 23 age > float_of_int days) 24 ) entries in 25 26 (* Print entries *) 27 List.iter (fun e -> 28 let open Cacheio.Entry in 29 let flags_str = Cacheio.Flags.pp Format.str_formatter (flags e); 30 Format.flush_str_formatter () in 31 let ttl_str = match ttl e with 32 | None -> "never" 33 | Some t -> Printf.sprintf "%.0f" t 34 in 35 let size = size e in 36 let size_str = 37 if size < 1024L then Printf.sprintf "%Ld B" size 38 else if size < 1048576L then Printf.sprintf "%.1f KB" (Int64.to_float size /. 1024.) 39 else Printf.sprintf "%.1f MB" (Int64.to_float size /. 1048576.) 40 in 41 Printf.printf "%s %s %s %s %s\n" 42 (String.sub (Cacheio.Entry.key e) 0 16) 43 size_str 44 ttl_str 45 flags_str 46 (if Cacheio.Entry.is_expired e then "[EXPIRED]" else "") 47 ) filtered; 48 49 Printf.printf "\nTotal: %d entries\n" (List.length filtered) 50 in 51 52 let stale = Arg.(value & flag & info ["stale"; "s"] ~doc:"Show only stale entries") in 53 let expired = Arg.(value & flag & info ["expired"; "e"] ~doc:"Show only expired entries") in 54 let pinned = Arg.(value & flag & info ["pinned"; "p"] ~doc:"Show only pinned entries") in 55 let temporary = Arg.(value & flag & info ["temporary"; "t"] ~doc:"Show only temporary entries") in 56 let older_than = 57 let doc = "Show entries older than N days" in 58 Arg.(value & opt (some int) None & info ["older-than"] ~docv:"DAYS" ~doc) 59 in 60 61 let doc = "List cache entries" in 62 let info = Cmd.info "list" ~doc in 63 let cache_dir_term = Xdge.Cmd.cache_term app_name in 64 Cmd.v info Term.(const list $ cache_dir_term $ stale $ expired $ pinned $ temporary $ older_than) 65 66let expire_cmd ~app_name fs _stdin = 67 let expire cache_dir = 68 let base_dir = Path.(fs / cache_dir) in 69 let cache = Cacheio.create ~base_dir in 70 let count = Cacheio.expire cache in 71 Printf.printf "Expired %d entries\n" count 72 in 73 74 let doc = "Remove expired entries from cache" in 75 let info = Cmd.info "expire" ~doc in 76 let cache_dir_term = Xdge.Cmd.cache_term app_name in 77 Cmd.v info Term.(const expire $ cache_dir_term) 78 79let clear_cmd ~app_name fs _stdin = 80 let clear cache_dir all temporary = 81 let base_dir = Path.(fs / cache_dir) in 82 let cache = Cacheio.create ~base_dir in 83 84 if all then begin 85 Cacheio.clear cache; 86 Printf.printf "Cleared all entries (except pinned)\n" 87 end else if temporary then begin 88 Cacheio.clear_temporary cache; 89 Printf.printf "Cleared temporary entries\n" 90 end else begin 91 Printf.printf "Specify --all or --temporary to clear cache\n"; 92 exit 1 93 end 94 in 95 96 let all = Arg.(value & flag & info ["all"; "a"] ~doc:"Clear all entries (except pinned)") in 97 let temporary = Arg.(value & flag & info ["temporary"; "t"] ~doc:"Clear only temporary entries") in 98 99 let doc = "Clear cache entries" in 100 let info = Cmd.info "clear" ~doc in 101 let cache_dir_term = Xdge.Cmd.cache_term app_name in 102 Cmd.v info Term.(const clear $ cache_dir_term $ all $ temporary) 103 104let flag_cmd ~app_name fs _stdin = 105 let flag cache_dir key add remove set_flags = 106 let base_dir = Path.(fs / cache_dir) in 107 let cache = Cacheio.create ~base_dir in 108 109 (* Parse flags *) 110 let parse_flag_str s = 111 match String.lowercase_ascii s with 112 | "pinned" | "p" -> Some `Pinned 113 | "stale" | "s" -> Some `Stale 114 | "temporary" | "t" -> Some `Temporary 115 | _ -> None 116 in 117 118 (* Handle operations *) 119 match key with 120 | None -> 121 Printf.printf "Error: key required\n"; 122 exit 1 123 | Some k -> 124 (* Add flags *) 125 List.iter (fun flag_str -> 126 match parse_flag_str flag_str with 127 | Some flag -> 128 Cacheio.add_flag cache ~key:k flag; 129 Printf.printf "Added flag %s to %s\n" flag_str k 130 | None -> 131 Printf.printf "Unknown flag: %s\n" flag_str 132 ) add; 133 134 (* Remove flags *) 135 List.iter (fun flag_str -> 136 match parse_flag_str flag_str with 137 | Some flag -> 138 Cacheio.remove_flag cache ~key:k flag; 139 Printf.printf "Removed flag %s from %s\n" flag_str k 140 | None -> 141 Printf.printf "Unknown flag: %s\n" flag_str 142 ) remove; 143 144 (* Set flags (replace all) *) 145 (match set_flags with 146 | [] -> () 147 | flags -> 148 let parsed = List.filter_map parse_flag_str flags in 149 Cacheio.set_flags cache ~key:k (Cacheio.Flags.of_list parsed); 150 Printf.printf "Set flags for %s\n" k) 151 in 152 153 let key = Arg.(value & pos 0 (some string) None & info [] ~docv:"KEY" ~doc:"Cache key to modify") in 154 let add = 155 let doc = "Add flag (pinned|stale|temporary)" in 156 Arg.(value & opt_all string [] & info ["add"; "a"] ~doc) 157 in 158 let remove = 159 let doc = "Remove flag (pinned|stale|temporary)" in 160 Arg.(value & opt_all string [] & info ["remove"; "r"] ~doc) 161 in 162 let set_flags = 163 let doc = "Set flags (replaces all existing)" in 164 Arg.(value & opt_all string [] & info ["set"] ~doc) 165 in 166 167 let doc = "Manage cache entry flags" in 168 let info = Cmd.info "flag" ~doc in 169 let cache_dir_term = Xdge.Cmd.cache_term app_name in 170 Cmd.v info Term.(const flag $ cache_dir_term $ key $ add $ remove $ set_flags) 171 172let stats_cmd ~app_name fs _stdin = 173 let stats cache_dir = 174 let base_dir = Path.(fs / cache_dir) in 175 let cache = Cacheio.create ~base_dir in 176 let stats = Cacheio.stats cache in 177 178 let size_str size = 179 if size < 1024L then Printf.sprintf "%Ld B" size 180 else if size < 1048576L then Printf.sprintf "%.1f KB" (Int64.to_float size /. 1024.) 181 else if size < 1073741824L then Printf.sprintf "%.1f MB" (Int64.to_float size /. 1048576.) 182 else Printf.sprintf "%.1f GB" (Int64.to_float size /. 1073741824.) 183 in 184 185 let open Cacheio.Stats in 186 Printf.printf "Cache Statistics:\n"; 187 Printf.printf " Total entries: %d\n" (entry_count stats); 188 Printf.printf " Total size: %s\n" (size_str (total_size stats)); 189 Printf.printf " Expired entries: %d\n" (expired_count stats); 190 Printf.printf " Pinned entries: %d\n" (pinned_count stats); 191 Printf.printf " Stale entries: %d\n" (stale_count stats); 192 Printf.printf " Temporary entries: %d\n" (temporary_count stats) 193 in 194 195 let doc = "Show cache statistics" in 196 let info = Cmd.info "stats" ~doc in 197 let cache_dir_term = Xdge.Cmd.cache_term app_name in 198 Cmd.v info Term.(const stats $ cache_dir_term) 199 200let put_cmd ~app_name fs stdin = 201 let put cache_dir key file ttl pinned stale temporary = 202 let base_dir = Path.(fs / cache_dir) in 203 let cache = Cacheio.create ~base_dir in 204 205 (* Build flags from options *) 206 let flags = 207 let open Cacheio.Flags in 208 let f = empty in 209 let f = if pinned then add `Pinned f else f in 210 let f = if stale then add `Stale f else f in 211 let f = if temporary then add `Temporary f else f in 212 f 213 in 214 215 (* Read from file or stdin *) 216 Switch.run @@ fun sw -> 217 let source = match file with 218 | Some path -> 219 let file_path = Path.(fs / path) in 220 (Path.open_in ~sw file_path :> Eio.Flow.source_ty Eio.Resource.t) 221 | None -> 222 (* Use stdin passed from the CLI *) 223 stdin 224 in 225 226 (* Put into cache *) 227 Cacheio.put cache ~key ~source ?ttl:(Some ttl) ~flags (); 228 Printf.printf "Cached entry with key: %s\n" key; 229 230 (* Show info about what was cached *) 231 match Cacheio.size cache ~key with 232 | Some size -> Printf.printf "Size: %Ld bytes\n" size 233 | None -> () 234 in 235 236 let key = Arg.(required & pos 0 (some string) None & info [] ~docv:"KEY" 237 ~doc:"Cache key for the entry") in 238 let file = Arg.(value & opt (some string) None & info ["file"; "f"] ~docv:"FILE" 239 ~doc:"File to cache (default: read from stdin)") in 240 let ttl = Arg.(value & opt (some float) None & info ["ttl"] ~docv:"SECONDS" 241 ~doc:"Time-to-live in seconds") in 242 let pinned = Arg.(value & flag & info ["pinned"; "p"] ~doc:"Mark entry as pinned") in 243 let stale = Arg.(value & flag & info ["stale"; "s"] ~doc:"Mark entry as stale") in 244 let temporary = Arg.(value & flag & info ["temporary"; "t"] ~doc:"Mark entry as temporary") in 245 246 let doc = "Put data into cache" in 247 let man = [ 248 `S "DESCRIPTION"; 249 `P "Store data in the cache with the specified key. Data can be read from a file or stdin."; 250 `P "Optionally set TTL (time-to-live) and flags (pinned, stale, temporary)."; 251 `S "EXAMPLES"; 252 `P "Cache a file: $(b,cache put mykey -f data.txt)"; 253 `P "Cache from stdin: $(b,echo 'Hello' | cache put mykey)"; 254 `P "Cache with TTL: $(b,cache put mykey -f data.txt --ttl 3600)"; 255 `P "Cache as pinned: $(b,cache put mykey -f data.txt --pinned)"; 256 ] in 257 let info = Cmd.info "put" ~doc ~man in 258 let cache_dir_term = Xdge.Cmd.cache_term app_name in 259 Cmd.v info Term.(const put $ cache_dir_term $ key $ file $ ttl $ pinned $ stale $ temporary) 260 261let get_cmd ~app_name fs _stdin = 262 let get cache_dir key output = 263 let base_dir = Path.(fs / cache_dir) in 264 let cache = Cacheio.create ~base_dir in 265 266 (* Get from cache *) 267 Switch.run @@ fun sw -> 268 match Cacheio.get cache ~key ~sw with 269 | None -> 270 Printf.eprintf "Key not found: %s\n" key; 271 exit 1 272 | Some source -> 273 (* Write to file or stdout *) 274 match output with 275 | Some path -> 276 let file_path = Path.(fs / path) in 277 let sink = Path.open_out ~sw ~create:(`Or_truncate 0o644) file_path in 278 Eio.Flow.copy source sink; 279 Printf.eprintf "Wrote cached data to: %s\n" path 280 | None -> 281 (* For stdout, we need to read the content and print it *) 282 let buf = Buffer.create 4096 in 283 Eio.Flow.copy source (Eio.Flow.buffer_sink buf); 284 print_string (Buffer.contents buf) 285 in 286 287 let key = Arg.(required & pos 0 (some string) None & info [] ~docv:"KEY" 288 ~doc:"Cache key to retrieve") in 289 let output = Arg.(value & opt (some string) None & info ["output"; "o"] ~docv:"FILE" 290 ~doc:"Output file (default: write to stdout)") in 291 292 let doc = "Get data from cache" in 293 let man = [ 294 `S "DESCRIPTION"; 295 `P "Retrieve data from the cache using the specified key. Data can be written to a file or stdout."; 296 `S "EXAMPLES"; 297 `P "Get to stdout: $(b,cache get mykey)"; 298 `P "Get to file: $(b,cache get mykey -o output.txt)"; 299 `P "Use in pipeline: $(b,cache get mykey | grep pattern)"; 300 ] in 301 let info = Cmd.info "get" ~doc ~man in 302 let cache_dir_term = Xdge.Cmd.cache_term app_name in 303 Cmd.v info Term.(const get $ cache_dir_term $ key $ output) 304 305(** Delete command - remove a specific cache entry *) 306let delete_cmd ~app_name fs _stdin = 307 let delete cache_dir key = 308 let base_dir = Path.(fs / cache_dir) in 309 let cache = Cacheio.create ~base_dir in 310 311 if Cacheio.exists cache ~key then begin 312 Cacheio.delete cache ~key; 313 Printf.printf "Deleted cache entry: %s\n" key 314 end else begin 315 Printf.eprintf "Key not found: %s\n" key; 316 exit 1 317 end 318 in 319 320 let key = Arg.(required & pos 0 (some string) None & info [] ~docv:"KEY" 321 ~doc:"Cache key to delete") in 322 323 let doc = "Delete a cache entry" in 324 let man = [ 325 `S "DESCRIPTION"; 326 `P "Remove a specific cache entry by key."; 327 `S "EXAMPLES"; 328 `P "Delete entry: $(b,cache delete mykey)"; 329 ] in 330 let info = Cmd.info "delete" ~doc ~man in 331 let cache_dir_term = Xdge.Cmd.cache_term app_name in 332 Cmd.v info Term.(const delete $ cache_dir_term $ key) 333 334(** Exists command - check if a key exists *) 335let exists_cmd ~app_name fs _stdin = 336 let exists cache_dir key quiet = 337 let base_dir = Path.(fs / cache_dir) in 338 let cache = Cacheio.create ~base_dir in 339 340 if Cacheio.exists cache ~key then begin 341 if not quiet then Printf.printf "Key exists: %s\n" key; 342 exit 0 343 end else begin 344 if not quiet then Printf.printf "Key does not exist: %s\n" key; 345 exit 1 346 end 347 in 348 349 let key = Arg.(required & pos 0 (some string) None & info [] ~docv:"KEY" 350 ~doc:"Cache key to check") in 351 let quiet = Arg.(value & flag & info ["quiet"; "q"] 352 ~doc:"Suppress output, use exit code only") in 353 354 let doc = "Check if a cache entry exists" in 355 let man = [ 356 `S "DESCRIPTION"; 357 `P "Check if a cache entry exists. Returns exit code 0 if exists, 1 if not."; 358 `S "EXAMPLES"; 359 `P "Check existence: $(b,cache exists mykey)"; 360 `P "Use in scripts: $(b,if cache exists mykey -q; then echo 'Found'; fi)"; 361 ] in 362 let info = Cmd.info "exists" ~doc ~man in 363 let cache_dir_term = Xdge.Cmd.cache_term app_name in 364 Cmd.v info Term.(const exists $ cache_dir_term $ key $ quiet) 365 366(** {1 Command Groups} *) 367 368let make_commands ~app_name fs stdin = [ 369 get_cmd ~app_name fs stdin; 370 put_cmd ~app_name fs stdin; 371 delete_cmd ~app_name fs stdin; 372 exists_cmd ~app_name fs stdin; 373 list_cmd ~app_name fs stdin; 374 expire_cmd ~app_name fs stdin; 375 clear_cmd ~app_name fs stdin; 376 flag_cmd ~app_name fs stdin; 377 stats_cmd ~app_name fs stdin 378] 379 380let make_cmd ~app_name fs stdin = 381 let doc = "Cache management commands" in 382 let info = Cmd.info "cache" ~doc in 383 Cmd.group info (make_commands ~app_name fs stdin)