summaryrefslogtreecommitdiff
path: root/bs5/universal/native/DB.ml
diff options
context:
space:
mode:
Diffstat (limited to 'bs5/universal/native/DB.ml')
-rw-r--r--bs5/universal/native/DB.ml120
1 files changed, 120 insertions, 0 deletions
diff --git a/bs5/universal/native/DB.ml b/bs5/universal/native/DB.ml
new file mode 100644
index 0000000..53ffa1b
--- /dev/null
+++ b/bs5/universal/native/DB.ml
@@ -0,0 +1,120 @@
+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)