open Lwt.Syntax let read_file file = let ( / ) = Filename.concat in let path = Sys.getcwd () / "server" / "db" / file in try%lwt let%lwt v = Lwt_io.with_file ~mode:Lwt_io.Input path Lwt_io.read in Lwt_result.return v with e -> Dream.log "Error reading file %s: %s" path (Printexc.to_string e); Lwt.return_error (Printexc.to_string e) let parse_note (note : Yojson.Safe.t) : Note.t option = match note with | `Assoc fields -> let id = fields |> List.assoc "id" |> Yojson.Safe.to_string |> int_of_string in let title = fields |> List.assoc "title" |> Yojson.Safe.Util.to_string in let content = fields |> List.assoc "content" |> Yojson.Safe.Util.to_string in let updated_at = fields |> List.assoc "updated_at" |> Yojson.Safe.to_string |> float_of_string in Some { Note.id; title; content; updated_at } | _ -> None let parse_notes json = try match Yojson.Safe.from_string json with | `List notes -> notes |> List.filter_map parse_note |> Result.ok | _ -> Result.error "Invalid notes file format" with _ -> Result.error "Invalid JSON format format" module Cache = struct let db_cache = ref None let set value = db_cache := Some value let read () = !db_cache let delete () = db_cache := None end let read_notes () = match Cache.read () with | Some (Ok notes) -> Lwt_result.return notes | Some (Error e) -> Lwt_result.fail e | None -> ( try%lwt match%lwt read_file "./notes.json" with | Ok json -> Cache.set (parse_notes json); Lwt_result.lift (parse_notes json) | Error _ -> Lwt.return_error "Error reading notes file" with _error -> (* When something fails, treat it as an empty note db *) Lwt.return_ok []) let find_one notes id = match notes |> List.find_opt (fun (note : Note.t) -> note.id = id) with | Some note -> Lwt_result.return note | None -> Lwt_result.fail ("Note with id " ^ Int.to_string id ^ " not found") let add_note ~title ~content = let%lwt notes = read_notes () in let notes = Result.map (fun notes -> let length = List.length notes in let note : Note.t = { id = length; title; content; updated_at = Unix.time () } in note :: notes) notes in Cache.set notes; Lwt_result.lift (notes |> Result.map (fun notes -> notes |> List.hd)) let edit_note ~id ~title ~content = let%lwt notes = read_notes () in let notes = Result.map (fun notes -> let notes = notes |> List.map (fun (current_note : Note.t) -> if current_note.id = id then { current_note with title; content; updated_at = Unix.time (); } else current_note) in notes) notes in Cache.set notes; Lwt_result.lift (notes |> Result.map (fun notes -> notes |> List.hd)) let delete_note id = let%lwt notes = read_notes () in let notes = Result.map (fun notes -> notes |> List.filter (fun (note : Note.t) -> note.id <> id)) notes in Cache.set notes; Lwt_result.lift notes let fetch_note id = match Cache.read () with | Some (Ok notes) -> find_one notes id | Some (Error e) -> Lwt_result.fail e | None -> ( let* notes = read_notes () in match notes with | Ok notes -> find_one notes id | Error e -> Lwt_result.fail e)