Author: Kevin Schoon [kevinschoon@gmail.com]
Hash: fae1c3683d7074488ef6f1f9e413ca051a8ab10e
Timestamp: Mon, 21 Jun 2021 19:09:29 +0000 (3 years ago)

+176 -1 +/-3 browse
WIP add manifest
1diff --git a/lib/manifest.ml b/lib/manifest.ml
2new file mode 100644
3index 0000000..1fa6d1f
4--- /dev/null
5+++ b/lib/manifest.ml
6 @@ -0,0 +1,139 @@
7+ open Core
8+
9+ module Item = struct
10+ type t = {
11+ parent : string option;
12+ slug : string;
13+ title : string;
14+ description : string;
15+ tags : string list;
16+ }
17+
18+ let make ~parent ~slug ~title ~description ~tags =
19+ { parent; slug; title; description; tags }
20+
21+ let of_json json =
22+ let slug = Ezjsonm.find json [ "slug" ] |> Ezjsonm.get_string in
23+ let title = Ezjsonm.find json [ "title" ] |> Ezjsonm.get_string in
24+ let description =
25+ Ezjsonm.find json [ "description" ] |> Ezjsonm.get_string
26+ in
27+ let tags = Ezjsonm.find json [ "tags" ] |> Ezjsonm.get_strings in
28+ let parent =
29+ match Ezjsonm.find_opt json [ "parent" ] with
30+ | Some parent -> Some (parent |> Ezjsonm.get_string)
31+ | None -> None
32+ in
33+ { slug; parent; title; description; tags }
34+
35+ let to_json item =
36+ let parent =
37+ match item.parent with
38+ | Some parent -> parent |> Ezjsonm.string
39+ | None -> Ezjsonm.unit ()
40+ in
41+ Ezjsonm.dict
42+ [
43+ ("parent", parent);
44+ ("slug", item.slug |> Ezjsonm.string);
45+ ("title", item.title |> Ezjsonm.string);
46+ ("description", item.description |> Ezjsonm.string);
47+ ("tags", item.tags |> Ezjsonm.strings);
48+ ]
49+ end
50+
51+ type t = { items : Item.t list }
52+
53+ let empty = { items = [] }
54+
55+ let of_json json =
56+ let items =
57+ Ezjsonm.find json [ "items" ]
58+ |> Ezjsonm.get_list (fun item -> item |> Item.of_json)
59+ in
60+ { items }
61+
62+ let to_json manifest =
63+ let items = Ezjsonm.list Item.to_json manifest.items in
64+ Ezjsonm.dict [ ("items", items) ]
65+
66+ let of_string manifest = manifest |> Ezjsonm.from_string |> of_json
67+
68+ let to_string manifest = manifest |> to_json |> Ezjsonm.to_string
69+
70+ let lock path =
71+ match path |> Sys.file_exists with
72+ | `Yes -> failwith "unable to aquire lock"
73+ | `No | `Unknown -> Out_channel.write_all ~data:"<locked>" path
74+
75+ let unlock path =
76+ match path |> Sys.file_exists with
77+ | `Yes -> Sys.remove path
78+ | `No | `Unknown -> ()
79+
80+ let lockfile path = Filename.concat (path |> Filename.dirname) "note.lock"
81+
82+ let load_or_init path =
83+ match Sys.file_exists path with
84+ | `Yes -> path |> In_channel.read_all |> of_string
85+ | `No | `Unknown ->
86+ path |> Out_channel.write_all ~data:(to_string empty);
87+ empty
88+
89+ let save ~path manifest =
90+ path |> lockfile |> lock;
91+ Out_channel.write_all ~data:(to_string manifest) path;
92+ path |> lockfile |> unlock
93+
94+ let rec to_path ~manifest (item : Item.t) =
95+ match item.parent with
96+ | Some parent_slug ->
97+ let parent =
98+ manifest.items
99+ |> List.find_exn ~f:(fun other -> String.equal other.slug parent_slug)
100+ in
101+ let base_path = parent |> to_path ~manifest in
102+ let base_path = Filename.concat base_path item.title in
103+ base_path
104+ | None -> Filename.concat "/" item.title
105+
106+ let exists ~path manifest =
107+ manifest.items
108+ |> List.exists ~f:(fun item -> item |> to_path ~manifest |> String.equal path)
109+
110+ let find ~path manifest =
111+ manifest.items
112+ |> List.find ~f:(fun item ->
113+ let file_path = item |> to_path ~manifest in
114+ String.equal file_path path)
115+
116+ let list ~path manifest =
117+ (* list items below path but not path itself *)
118+ manifest.items
119+ |> List.filter ~f:(fun item ->
120+ let item_path = item |> to_path ~manifest in
121+ String.equal path (Filename.dirname item_path))
122+
123+ let insert ~path ~slug ~title ~description ~tags manifest =
124+ match path with
125+ | "" | "/" ->
126+ let item = Item.make ~parent:None ~slug ~title ~description ~tags in
127+ if manifest |> exists ~path:(item |> to_path ~manifest) then
128+ failwith "duplicate item"
129+ else
130+ let items = item :: manifest.items in
131+ { items }
132+ | path ->
133+ let parent =
134+ match manifest |> find ~path with
135+ | Some parent -> parent.slug
136+ | None -> failwith "no parent"
137+ in
138+ let item =
139+ Item.make ~parent:(Some parent) ~slug ~title ~description ~tags
140+ in
141+ if manifest |> exists ~path:(item |> to_path ~manifest) then
142+ failwith "duplicate item"
143+ else
144+ let items = item :: manifest.items in
145+ { items }
146 diff --git a/test/dune b/test/dune
147index d6c1f27..eaab266 100644
148--- a/test/dune
149+++ b/test/dune
150 @@ -1,4 +1,4 @@
151 (tests
152- (names config_test display_test note_test slug_test)
153+ (names config_test display_test manifest_test note_test slug_test)
154 (libraries note_lib alcotest)
155 )
156 diff --git a/test/manifest_test.ml b/test/manifest_test.ml
157new file mode 100644
158index 0000000..45ead24
159--- /dev/null
160+++ b/test/manifest_test.ml
161 @@ -0,0 +1,36 @@
162+ open Core
163+ open Note_lib
164+
165+ let test_manifest () =
166+ let temp_db = Filename.temp_file "note-test" "" in
167+ Manifest.empty |> Manifest.save ~path:temp_db;
168+ let manifest =
169+ Manifest.load_or_init temp_db
170+ |> Manifest.insert ~path:"/" ~slug:"note-00000000-0.md" ~title:"fuu"
171+ ~description:"" ~tags:[]
172+ in
173+ let result = manifest |> Manifest.find ~path:"/fuu" in
174+ Alcotest.(check bool) "manifest loaded" (result |> Option.is_some) true;
175+ let manifest =
176+ manifest
177+ |> Manifest.insert ~path:"/fuu" ~slug:"note-00000000-1.md" ~title:"bar"
178+ ~description:"" ~tags:[]
179+ in
180+ print_endline (Manifest.to_string manifest);
181+ let result = manifest |> Manifest.find ~path:"/fuu/bar" in
182+ Alcotest.(check bool) "manifest loaded" (result |> Option.is_some) true;
183+ let result_path = Option.value_exn result |> Manifest.to_path ~manifest in
184+ Alcotest.(check string) "result path" "/fuu/bar" result_path;
185+ let manifest =
186+ manifest
187+ |> Manifest.insert ~path:"/fuu" ~slug:"note-00000000-2.md" ~title:"baz"
188+ ~description:"" ~tags:[]
189+ in
190+ let results = manifest |> Manifest.list ~path:"/fuu" in
191+ Alcotest.(check int) "n_results" 2 (List.length results)
192+
193+ let () =
194+ Alcotest.run "Config"
195+ [
196+ ("load", [ Alcotest.test_case "test manifest" `Quick test_manifest ]);
197+ ]