Command-line and Emacs Calendar Client
1open Eio 2open Cmdliner 3open Caledonia_lib 4open Caledonia_lib.Sexp 5 6let run ~stdin ~stdout ~fs calendar_dir () = 7 let reader = Buf_read.of_flow stdin ~max_size:1_000_000 in 8 let ( let* ) = Result.bind in 9 10 (* Initialize mutable events variable - will be updated on refresh *) 11 let mutable_events = ref (Calendar_dir.get_events ~fs calendar_dir) in 12 13 try 14 while true do 15 let line = Buf_read.line reader in 16 let response = 17 try 18 let sexp = Sexplib.Sexp.of_string line in 19 let request = Sexp.request_of_sexp sexp in 20 match request with 21 | ListCalendars -> 22 let* names = Calendar_dir.list_calendar_names ~fs calendar_dir in 23 Ok (sexp_of_response (Ok (Calendars names))) 24 | Refresh -> 25 (* Reload events from disk *) 26 mutable_events := Calendar_dir.get_events ~fs calendar_dir; 27 (* Return an empty response *) 28 Ok (sexp_of_response (Ok Empty)) 29 | Query query_req -> 30 let* filter, from, to_, limit, _tz = 31 generate_query_params query_req 32 in 33 let* events = !mutable_events in 34 let events = Event.query events ~filter ~from ~to_ ?limit () in 35 Ok (sexp_of_response (Ok (Events events))) 36 with 37 | Sexplib.Conv.Of_sexp_error (_exn, bad_sexp) -> 38 let msg = 39 Printf.sprintf "Invalid request format for '%s': %s" line 40 (to_string bad_sexp) 41 in 42 Ok (sexp_of_response (Error msg)) 43 | Failure msg -> 44 Ok (sexp_of_response (Error ("Processing failed: " ^ msg))) 45 | exn -> 46 let msg = 47 Printf.sprintf "Unexpected error: %s" (Printexc.to_string exn) 48 in 49 Ok (sexp_of_response (Error msg)) 50 in 51 let response_line = 52 to_string 53 (match response with 54 | Ok r -> r 55 | Error (`Msg msg) -> Sexp.sexp_of_response (Error msg)) 56 in 57 Flow.copy_string (response_line ^ "\n") stdout 58 done 59 with End_of_file -> () 60 61let cmd ~stdin ~stdout ~fs calendar_dir = 62 let run () = 63 let _ = run ~stdin ~stdout ~fs calendar_dir () in 64 0 65 in 66 let term = Term.(const run) in 67 68 let doc = "Process single-line S-expression requests from stdin to stdout." in 69 let man = 70 [ 71 `S Manpage.s_description; 72 `P 73 "$(mname) $(tname) reads S-expression requests (one per line) from \ 74 stdin, processes them, and writes S-expression responses (one per \ 75 line) to stdout."; 76 `P "Example request: '(Query (()))'"; 77 `P 78 "Example response: '(Ok (Events ((id ...) (summary ...) ...)))' or \ 79 '(Error \"...\")'"; 80 `S Manpage.s_examples; 81 `Pre "echo '(Query ((text \\\"meeting\\\")))' | $(mname) $(tname)"; 82 ] 83 in 84 let info = Cmd.info "server" ~doc ~man in 85 Cmd.v info term