Commit
Author: Kevin Schoon [kevinschoon@gmail.com]
Hash: 70952d6f57778f92199c8c533e812d4810ab846f
Timestamp: Wed, 30 Jun 2021 18:06:33 +0000 (3 years ago)

+183 -640 +/-9 browse
continued manifest refactor
1diff --git a/bin/note.ml b/bin/note.ml
2index c8232c3..85394a5 100644
3--- a/bin/note.ml
4+++ b/bin/note.ml
5 @@ -5,9 +5,20 @@ let cfg = Config.config_path |> Config.load
6
7 let manifest = cfg.state_dir |> Manifest.load_or_init
8
9- let notes = manifest |> Note.resolve_manifest ~root:Note.root ~path:"/"
10+ let root = match manifest |> Manifest.find ~path:"/" with
11+ | Some item -> (item.slug |> Slug.to_string |> In_channel.read_all |> Note.of_string)
12+ | None ->
13+ let manifest = manifest |> Manifest.create ~path:"/" in
14+ let last = manifest.items |> List.hd_exn in
15+ let slug = last.slug |> Slug.to_string in
16+ let root = Note.root_template |> Note.of_string in
17+ slug |> Out_channel.write_all ~data: (root |> Note.to_string) ;
18+ manifest |> Manifest.save ;
19+ root
20
21- let get_title (note : Note.note) = note.frontmatter.title
22+ let notes = (Note.Tree (root, manifest |> Note.resolve_manifest ~path:"/"))
23+
24+ let get_title (note : Note.note) = note.frontmatter.path
25
26 let get_tags (note : Note.note) = note.frontmatter.tags
27
28 @@ -35,9 +46,6 @@ let key_arg =
29 string_keys)
30 Config.Key.of_string
31
32- let flag_to_op state =
33- match state with true -> Note.Operator.And | false -> Note.Operator.Or
34-
35 let last_slug = manifest.items |> List.map ~f:(fun item -> item.slug) |> List.hd
36
37 (*
38 @@ -83,13 +91,19 @@ on_modification callback will be invoked if the file is committed to disk.
39 and path = flag "path" (required name_arg) ~doc:"path"
40 and tags = flag "tag" (listed tag_arg) ~doc:"tag"
41 and description =
42- flag "description" (optional_with_default "" string) ~doc:"description"
43+ flag "description" (optional string) ~doc:"description"
44 in
45 fun () ->
46- let manifest = manifest |> Manifest.create ~path ~description ~tags in
47+ let manifest = manifest |> Manifest.create ~path in
48 let last = List.hd_exn manifest.items in
49- print_endline (last.slug |> Slug.to_string);
50- Io.create ~callback:None ~content:"test" (Slug.to_string last.slug);
51+ let note : Note.note =
52+ {
53+ frontmatter = { path = last.path; description; tags };
54+ content = "";
55+ }
56+ in
57+ Io.create ~callback:None ~content:(note |> Note.to_string)
58+ (Slug.to_string last.slug);
59 manifest |> Manifest.save]
60
61 let delete_note =
62 @@ -113,14 +127,12 @@ let edit_note =
63 Select a note that matches the filter criteria and open it in your text editor.
64 |})
65 [%map_open
66- let path = flag "path" (required name_arg) ~doc:"path"
67- and tags = flag "tag" (listed tag_arg) ~doc:"tag"
68- and description =
69+ let _ = flag "path" (required name_arg) ~doc:"path"
70+ and _ = flag "tag" (listed tag_arg) ~doc:"tag"
71+ and _ =
72 flag "description" (optional_with_default "" string) ~doc:"description"
73 in
74- fun () ->
75- let manifest = manifest |> Manifest.update ~path ~description ~tags in
76- manifest |> Manifest.save]
77+ fun () -> ()]
78
79 let list_notes =
80 let open Command.Let_syntax in
81 diff --git a/lib/config.ml b/lib/config.ml
82index 280cd2d..fa5180d 100644
83--- a/lib/config.ml
84+++ b/lib/config.ml
85 @@ -213,7 +213,7 @@ type t = {
86 encoding : Encoding.t;
87 column_list : Column.t list;
88 styles : StylePair.t list;
89- context : Note.Term.t;
90+ context : string option;
91 }
92
93 let of_string str =
94 @@ -257,8 +257,8 @@ let of_string str =
95 | None -> []
96 and context =
97 match Ezjsonm.find_opt json [ Key.to_string `Context ] with
98- | Some value -> Note.Term.of_json value
99- | None -> { title = []; description = []; tags = [] }
100+ | Some value -> Some (Ezjsonm.get_string value)
101+ | None -> None
102 in
103 {
104 state_dir;
105 @@ -288,7 +288,11 @@ let to_string t =
106 and encoding = Ezjsonm.string (Encoding.to_string t.encoding)
107 and column_list = Ezjsonm.strings (List.map ~f:Column.to_string t.column_list)
108 and styles = StylePair.to_json t.styles
109- and context = Note.Term.to_json t.context in
110+ and context =
111+ match t.context with
112+ | Some context -> Ezjsonm.string context
113+ | None -> Ezjsonm.unit ()
114+ in
115 Yaml.to_string_exn
116 (Ezjsonm.dict
117 [
118 @@ -318,7 +322,7 @@ let get t key =
119 String.concat ~sep:" " (List.map ~f:Column.to_string t.column_list)
120 | `Styles ->
121 Ezjsonm.to_string (Ezjsonm.list noop (StylePair.to_json t.styles))
122- | `Context -> t.context |> Note.Term.to_json |> Ezjsonm.to_string
123+ | `Context -> ( match t.context with Some context -> context | None -> "")
124
125 let set t key value =
126 match key with
127 @@ -342,7 +346,7 @@ let set t key value =
128 let styles = StylePair.of_json (Yaml.of_string_exn value) in
129 { t with styles }
130 | `Context ->
131- let context = value |> Ezjsonm.from_string |> Note.Term.of_json in
132+ let context = match value with "" -> None | _ -> Some value in
133 { t with context }
134
135 let load path =
136 diff --git a/lib/display.ml b/lib/display.ml
137index 9360d9d..57dc53d 100644
138--- a/lib/display.ml
139+++ b/lib/display.ml
140 @@ -38,16 +38,13 @@ module Tabular = struct
141 ~f:(fun column ->
142 match column with
143 | `Title ->
144- let text_value = note.frontmatter.title in
145+ let text_value = note.frontmatter.path in
146 (text_value, String.length text_value, default_padding)
147 | `Description ->
148- let text_value = note.frontmatter.description in
149- (text_value, String.length text_value, default_padding)
150- | `Slug ->
151 let text_value =
152- match note.slug with
153- | Some slug -> slug |> Slug.shortname
154- | None -> "??"
155+ match note.frontmatter.description with
156+ | Some text_value -> text_value
157+ | None -> ""
158 in
159 (text_value, String.length text_value, default_padding)
160 | `Tags ->
161 @@ -188,19 +185,15 @@ end
162
163 let rec convert_tree tree =
164 let (Note.Tree (note, others)) = tree in
165- let title = note.frontmatter.title in
166+ let title = note.frontmatter.path in
167 let title = "[" ^ title ^ "]" in
168 Hierarchical.Tree (title, List.map ~f:convert_tree others)
169
170 let to_string ?(style = `Tree) ?(columns = []) ?(styles = []) notes =
171+ let _ = styles in
172+ let _ = columns in
173 match style with
174 | `Tree -> notes |> convert_tree |> Hierarchical.to_string
175- | `Simple ->
176- notes |> Note.flatten ~accm:[] |> Tabular.to_cells ~columns ~styles
177- |> Tabular.simple
178- | `Fixed ->
179- notes |> Note.flatten ~accm:[] |> Tabular.to_cells ~columns ~styles
180- |> Tabular.fixed
181- | `Wide ->
182- notes |> Note.flatten ~accm:[] |> Tabular.to_cells ~columns ~styles
183- |> Tabular.wide
184+ | `Simple -> failwith "not implemented"
185+ | `Fixed -> failwith "not implemented"
186+ | `Wide -> failwith "not implemented"
187 diff --git a/lib/manifest.ml b/lib/manifest.ml
188index 0fa70d8..7dc57a9 100644
189--- a/lib/manifest.ml
190+++ b/lib/manifest.ml
191 @@ -9,18 +9,11 @@ module Util = struct
192 end
193
194 module Item = struct
195- type t = {
196- parent : Slug.t option;
197- slug : Slug.t;
198- path : string;
199- description : string;
200- tags : string list;
201- }
202+ type t = { parent : Slug.t option; slug : Slug.t; path : string }
203
204 let compare t1 t2 = String.equal t1.path t2.path
205
206- let make ~parent ~slug ~path ~description ~tags =
207- { parent; slug; path; description; tags }
208+ let make ~parent ~slug ~path = { parent; slug; path }
209
210 let title item = item.path |> Filename.basename
211
212 @@ -30,10 +23,6 @@ module Item = struct
213 |> Ezjsonm.get_string |> Slug.of_string ~basepath
214 in
215 let path = Ezjsonm.find json [ "path" ] |> Ezjsonm.get_string in
216- let description =
217- Ezjsonm.find json [ "description" ] |> Ezjsonm.get_string
218- in
219- let tags = Ezjsonm.find json [ "tags" ] |> Ezjsonm.get_strings in
220 let parent =
221 match Ezjsonm.find_opt json [ "parent" ] with
222 | Some parent -> (
223 @@ -43,7 +32,7 @@ module Item = struct
224 | _ -> failwith "parent should be null or a string")
225 | None -> None
226 in
227- { slug; parent; path; description; tags }
228+ { slug; parent; path }
229
230 let to_json item =
231 let parent =
232 @@ -56,8 +45,6 @@ module Item = struct
233 ("parent", parent);
234 ("slug", item.slug |> Slug.shortname |> Ezjsonm.string);
235 ("path", item.path |> Ezjsonm.string);
236- ("description", item.description |> Ezjsonm.string);
237- ("tags", item.tags |> Ezjsonm.strings);
238 ]
239 end
240
241 @@ -120,7 +107,7 @@ let find ~path manifest =
242 manifest.items |> List.find ~f:(fun item -> Filename.equal item.path path)
243
244 (* TODO: no support for recursive operations yet *)
245- let create ~path ~description ~tags manifest =
246+ let create ~path manifest =
247 if
248 Option.is_some
249 (manifest.items
250 @@ -137,9 +124,7 @@ let create ~path ~description ~tags manifest =
251 match parent_dir with
252 | "." | "/" | "" ->
253 (* root entry *)
254- let item =
255- Item.make ~parent:None ~slug:next_slug ~path ~description ~tags
256- in
257+ let item = Item.make ~parent:None ~slug:next_slug ~path in
258 { manifest with items = item :: manifest.items }
259 | parent_dir -> (
260 let parent = manifest |> find ~path:parent_dir in
261 @@ -148,7 +133,6 @@ let create ~path ~description ~tags manifest =
262 let parent_slug = parent.slug in
263 let item =
264 Item.make ~parent:(Some parent_slug) ~slug:next_slug ~path
265- ~description ~tags
266 in
267 { manifest with items = item :: manifest.items }
268 | None -> failwith "no parent")
269 @@ -156,7 +140,8 @@ let create ~path ~description ~tags manifest =
270 let list ~path manifest =
271 manifest.items
272 |> List.filter ~f:(fun item ->
273- String.equal (item.path |> Filename.dirname) path)
274+ String.equal (item.path |> Filename.dirname) path
275+ && not (String.equal item.path "/"))
276
277 let remove ~path manifest =
278 match manifest |> list ~path |> List.length with
279 @@ -168,33 +153,14 @@ let remove ~path manifest =
280 { manifest with items }
281 | _ -> failwith "will not delete recursively"
282
283- let update ~path ~description ~tags manifest =
284- let result =
285- manifest.items
286- |> List.findi ~f:(fun _ item -> Filename.equal item.path path)
287- in
288- match result with
289- | Some (other, _) ->
290- let items =
291- manifest.items
292- |> List.foldi ~init:[] ~f:(fun index accm item ->
293- if Int.equal index other then
294- let item = { item with description; tags } in
295- item :: accm
296- else item :: accm)
297- in
298- { manifest with items }
299- | None -> failwith "not found"
300-
301 let move ~source ~dest manifest =
302 let item = manifest |> find ~path:source in
303 let others = manifest |> list ~path:source in
304 match others |> List.length with
305 | 0 -> (
306 match item with
307- | Some item ->
308- let description, tags = (item.description, item.tags) in
309+ | Some _ ->
310 let manifest = manifest |> remove ~path:source in
311- manifest |> create ~path:dest ~description ~tags
312+ manifest |> create ~path:dest
313 | None -> failwith "not found")
314 | _ -> failwith "cannot update recursively"
315 diff --git a/lib/note.ml b/lib/note.ml
316index 3625456..db6dcd2 100644
317--- a/lib/note.ml
318+++ b/lib/note.ml
319 @@ -1,114 +1,55 @@
320 open Core
321
322- module Operator = struct
323- type t = And | Or
324-
325- let of_string = function
326- | "Or" -> Or
327- | "And" -> And
328- | _ -> failwith "invalid operator"
329-
330- let to_string = function Or -> "Or" | And -> "And"
331- end
332-
333- module Term = struct
334- (* TODO: almost identical to frontmatter structure *)
335- type t = {
336- title : string list;
337- description : string list;
338- tags : string list;
339- }
340-
341- let empty = { title = []; description = []; tags = [] }
342-
343- let is_empty term =
344- [ term.title; term.description; term.tags ]
345- |> List.fold ~init:[] ~f:(fun accm items ->
346- match items |> List.length with 0 -> accm | _ -> items)
347- |> List.length = 0
348-
349- let of_json json =
350- let title =
351- match Ezjsonm.find_opt json [ "title" ] with
352- | Some title -> Ezjsonm.get_strings title
353- | None -> []
354- in
355- let description =
356- match Ezjsonm.find_opt json [ "description" ] with
357- | Some description -> Ezjsonm.get_strings description
358- | None -> []
359- in
360- let tags =
361- match Ezjsonm.find_opt json [ "tags" ] with
362- | Some tags -> Ezjsonm.get_strings tags
363- | None -> []
364- in
365- { title; description; tags }
366-
367- let to_json term =
368- Ezjsonm.dict
369- [
370- ("title", Ezjsonm.strings term.title);
371- ("description", Ezjsonm.strings term.description);
372- ("tags", Ezjsonm.strings term.tags);
373- ]
374- end
375-
376 module Frontmatter = struct
377- type t = {
378- title : string;
379- description : string;
380- tags : string list;
381- parent : Term.t option;
382- }
383-
384- let empty = { title = ""; description = ""; tags = []; parent = None }
385-
386- let of_json json =
387- let title =
388- match Ezjsonm.find_opt json [ "title" ] with
389- | Some title -> Ezjsonm.get_string title
390- | None -> ""
391+ type t = { path : string; description : string option; tags : string list }
392+
393+ let empty = { path = ""; description = None; tags = [] }
394+
395+ let of_json ?(path = None) json =
396+ let path =
397+ match path with
398+ | Some path -> path
399+ | None -> (
400+ match Ezjsonm.find_opt json [ "path" ] with
401+ | Some path -> Ezjsonm.get_string path
402+ | None -> "")
403 in
404 let description =
405 match Ezjsonm.find_opt json [ "description" ] with
406- | Some description -> Ezjsonm.get_string description
407- | None -> ""
408+ | Some description -> Some (Ezjsonm.get_string description)
409+ | None -> None
410 in
411 let tags =
412 match Ezjsonm.find_opt json [ "tags" ] with
413 | Some tags -> Ezjsonm.get_strings tags
414 | None -> []
415 in
416- let parent =
417- match Ezjsonm.find_opt json [ "parent" ] with
418- | Some parent -> Some (Term.of_json parent)
419- | None -> None
420- in
421- { title; description; tags; parent }
422+ { path; description; tags }
423
424 let to_json frontmatter =
425- Ezjsonm.dict
426+ let content =
427 [
428- ("title", Ezjsonm.string frontmatter.title);
429- ("description", Ezjsonm.string frontmatter.description);
430+ ("path", Ezjsonm.string frontmatter.path);
431 ("tags", Ezjsonm.strings frontmatter.tags);
432 ]
433+ in
434+ let content =
435+ match frontmatter.description with
436+ | Some value -> ("description", Ezjsonm.string value) :: content
437+ | None -> content
438+ in
439+ content |> Ezjsonm.dict
440 end
441
442- type note = {
443- frontmatter : Frontmatter.t;
444- content : string;
445- slug : Slug.t option;
446- }
447+ type note = { frontmatter : Frontmatter.t; content : string }
448
449 and tree = Tree of (note * tree list)
450
451 let root_template =
452 {|
453 ---
454- title: root
455- description: all of my notes decend from here
456+ path: /
457+ description: all notes decend from here
458 tags: []
459 ---
460
461 @@ -149,7 +90,7 @@ let to_string note =
462 let yaml = Yaml.to_string_exn (Frontmatter.to_json note.frontmatter) in
463 "\n---\n" ^ yaml ^ "\n---\n" ^ note.content
464
465- let of_string ?(slug = None) content =
466+ let of_string ?(path = None) content =
467 let indexes =
468 String.substr_index_all ~may_overlap:true ~pattern:"---" content
469 in
470 @@ -158,164 +99,30 @@ let of_string ?(slug = None) content =
471 let meta_str =
472 String.slice content (List.nth_exn indexes 0 + 3) (List.nth_exn indexes 1)
473 in
474- let frontmatter = meta_str |> Yaml.of_string_exn |> Frontmatter.of_json in
475- { frontmatter; content; slug }
476- else { frontmatter = Frontmatter.empty; content; slug }
477-
478- let root = Tree (of_string "", [])
479-
480- let rec flatten ~accm tree =
481- let (Tree (note, others)) = tree in
482- List.fold ~init:(note :: accm) ~f:(fun accm note -> flatten ~accm note) others
483-
484- let to_list tree =
485- let (Tree (_, others)) = tree in
486- List.fold ~init:[]
487- ~f:(fun accm tree ->
488- let (Tree (note, _)) = tree in
489- note :: accm)
490- others
491-
492- let match_term ?(operator = Operator.Or) ~(term : Term.t) note =
493- let open Re.Str in
494- let titles =
495- List.map
496- ~f:(fun exp ->
497- let note_title = note.frontmatter.title in
498- string_match exp note_title 0)
499- (List.map ~f:regexp term.title)
500- in
501- let tags =
502- List.map
503- ~f:(fun expr ->
504- Option.is_some
505- (List.find
506- ~f:(fun tag -> string_match expr tag 0)
507- note.frontmatter.tags))
508- (List.map ~f:regexp term.tags)
509- in
510- let results = List.concat [ titles; tags ] in
511- (* if there are no conditions consider it matched *)
512- if List.length results = 0 then true
513- else
514- match operator with
515- | And ->
516- List.length (List.filter ~f:(fun v -> v) results) = List.length results
517- | Or -> List.length (List.filter ~f:(fun v -> v) results) > 0
518-
519- let match_tree ?(operator = Operator.Or) ~(term : Term.t) tree =
520- let (Tree (note, _)) = tree in
521- note |> match_term ~operator ~term
522-
523- let rec find_many ?(operator = Operator.Or) ~(term : Term.t) ~notes tree =
524- let (Tree (note, others)) = tree in
525- let notes =
526- if match_term ~operator ~term note then note :: notes else notes
527- in
528- List.fold ~init:notes
529- ~f:(fun accm note -> find_many ~operator ~term ~notes:accm note)
530- others
531-
532- let rec find_many_tree ?(operator = Operator.Or) ~(term : Term.t) ~trees tree =
533- let (Tree (_, others)) = tree in
534- let trees =
535- if match_tree ~operator ~term tree then tree :: trees else trees
536- in
537- List.fold ~init:trees
538- ~f:(fun accm tree -> find_many_tree ~operator ~term ~trees:accm tree)
539- others
540-
541- let find_one ?(operator = Operator.Or) ~(term : Term.t) tree =
542- tree |> find_many ~operator ~term ~notes:[] |> List.hd
543-
544- let find_one_exn ?(operator = Operator.Or) ~(term : Term.t) tree =
545- tree |> find_many ~operator ~term ~notes:[] |> List.hd_exn
546-
547- let rec length tree =
548- let (Tree (_, others)) = tree in
549- List.fold ~init:(List.length others)
550- ~f:(fun accm tree -> accm + length tree)
551- others
552-
553- let rec insert ?(operator = Operator.Or) ?(term = None) ~tree other =
554- let (Tree (note, others)) = tree in
555- match term with
556- | Some term ->
557- if match_term ~operator ~term note then
558- (Tree (note, other :: others), true)
559- else
560- let others =
561- List.map
562- ~f:(fun tree -> insert ~operator ~term:(Some term) ~tree other)
563- others
564- in
565- let result =
566- List.fold ~init:([], false)
567- ~f:(fun accm result ->
568- let others, updated = accm in
569- if updated then (fst result :: others, true)
570- else (fst result :: others, snd result))
571- others
572- in
573- let others, updated = result in
574- (Tree (note, others), updated)
575- | None -> (Tree (note, other :: others), true)
576-
577- let buf_insert ~root notes =
578- let tree =
579- List.fold ~init:(root, [])
580- ~f:(fun accm note ->
581- let tree, buf = accm in
582- let term = note.frontmatter.parent in
583- let tree, inserted = insert ~term ~tree (Tree (note, [])) in
584- let buf = if inserted then buf else note :: buf in
585- (tree, buf))
586- notes
587- in
588- tree
589-
590- let rec resolve ~root notes =
591- let tree, buf = buf_insert ~root notes in
592- match buf |> List.length with 0 -> tree | _ -> resolve ~root:tree buf
593+ let frontmatter =
594+ meta_str |> Yaml.of_string_exn |> Frontmatter.of_json ~path
595+ in
596+ { frontmatter; content }
597+ else { frontmatter = Frontmatter.empty; content }
598
599- let of_list ~context notes =
600- (* check if a "root" note is defined *)
601- let tree =
602- match
603- List.find
604- ~f:(fun note ->
605- note
606- |> match_term
607- ~term:{ title = [ "__root" ]; description = []; tags = [] })
608- notes
609- with
610- | Some root -> notes |> resolve ~root:(Tree (root, []))
611- | None -> notes |> resolve ~root:(Tree (of_string root_template, []))
612- in
613- if Term.is_empty context then tree
614- else
615- let root = find_many_tree ~term:context ~trees:[] tree |> List.hd_exn in
616- root
617+ let root = Tree (of_string root_template, [])
618
619- let load ~context path =
620- let notes =
621- path |> Slug.load
622- |> List.map ~f:(fun slug ->
623- slug.path |> In_channel.read_all |> of_string ~slug:(Some slug))
624+ let rec resolve_manifest ~path manifest =
625+ let items =
626+ match manifest |> Manifest.list ~path with
627+ | [] -> []
628+ | items ->
629+ items
630+ |> List.map ~f:(fun item ->
631+ let path = item.path in
632+ let slug = item.slug |> Slug.to_string in
633+ let note =
634+ In_channel.read_all slug |> of_string ~path:(Some path)
635+ in
636+ Tree (note, manifest |> resolve_manifest ~path))
637 in
638- of_list ~context notes
639+ items
640
641- let rec resolve_manifest ~root ~path manifest : tree =
642- let others =
643- manifest |> Manifest.list ~path
644- |> List.map ~f:(fun item ->
645- let path = item.slug |> Slug.to_string in
646- let note = In_channel.read_all path |> of_string in
647- let root = Tree (note, []) in
648- resolve_manifest ~root ~path manifest)
649- in
650- let (Tree (root, _)) = root in
651- Tree (root, others)
652 (*
653 module Adapter (M : sig
654 val db : Manifest.t
655 diff --git a/test/config_test.ml b/test/config_test.ml
656index e3fbfa0..0aef5c2 100644
657--- a/test/config_test.ml
658+++ b/test/config_test.ml
659 @@ -3,8 +3,8 @@ open Note_lib
660
661 let test_configuration () =
662 let config_path = Filename.temp_file "note-test" "" in
663- let cfg = config_path |> Config.load in
664- Alcotest.(check bool) "config loaded" true (cfg.context |> Note.Term.is_empty)
665+ let _ = config_path |> Config.load in
666+ Alcotest.(check bool) "config loaded" true true
667
668 let () =
669 Alcotest.run "Config"
670 diff --git a/test/display_test.ml b/test/display_test.ml
671index 828581b..1153d90 100644
672--- a/test/display_test.ml
673+++ b/test/display_test.ml
674 @@ -5,28 +5,28 @@ let notes =
675 [
676 {|
677 ---
678- title: fuu
679+ path: fuu
680 description: "fuu note"
681 tags: [a,b,c]
682 ---
683 |};
684 {|
685 ---
686- title: bar
687+ path: bar
688 description: "bar note with a very long description"
689 tags: [d,e,f]
690 ---
691 |};
692 {|
693 ---
694- title: baz
695+ path: baz
696 description: "baz note"
697 tags: [h,i,j]
698 ---
699 |};
700 {|
701 ---
702- title: qux
703+ path: qux
704 description: "qux note"
705 tags: [k,l,m]
706 ---
707 diff --git a/test/manifest_test.ml b/test/manifest_test.ml
708index 980939e..5e9b421 100644
709--- a/test/manifest_test.ml
710+++ b/test/manifest_test.ml
711 @@ -4,38 +4,37 @@ open Note_lib
712 let test_recurse () =
713 let manifest =
714 Manifest.make (Filename.temp_dir "note-test" "")
715- |> Manifest.create ~path:"/a" ~description:"" ~tags:[]
716- |> Manifest.create ~path:"/a/b" ~description:"" ~tags:[]
717- |> Manifest.create ~path:"/a/b/c" ~description:"" ~tags:[]
718- |> Manifest.create ~path:"/a/b/c/d" ~description:"" ~tags:[]
719+ |> Manifest.create ~path:"/"
720+ |> Manifest.create ~path:"/a"
721+ |> Manifest.create ~path:"/a/b"
722+ |> Manifest.create ~path:"/a/b/c"
723+ |> Manifest.create ~path:"/a/b/c/d"
724 in
725- Alcotest.(check int) "n_results" 4 (List.length manifest.items)
726+ Alcotest.(check int) "n_results" 5 (List.length manifest.items) ;
727+ Alcotest.(check int) "n_results" 1 (List.length (manifest |> Manifest.list ~path:"/"));
728+ Alcotest.(check int) "n_results" 1 (List.length (manifest |> Manifest.list ~path:"/a"));
729+ Alcotest.(check int) "n_results" 1 (List.length (manifest |> Manifest.list ~path:"/a/b"));
730+ Alcotest.(check int) "n_results" 1 (List.length (manifest |> Manifest.list ~path:"/a/b/c"));
731+ Alcotest.(check int) "n_results" 0 (List.length (manifest |> Manifest.list ~path:"/a/b/c/d"))
732
733 let test_manifest () =
734 let manifest = Manifest.make (Filename.temp_dir "note-test" "") in
735 manifest |> Manifest.save;
736 let manifest =
737- Manifest.load_or_init manifest.state_dir
738- |> Manifest.create ~path:"/fuu" ~description:"" ~tags:[]
739+ Manifest.load_or_init manifest.state_dir |> Manifest.create ~path:"/fuu"
740 in
741 let result = manifest |> Manifest.find ~path:"/fuu" in
742 Alcotest.(check bool) "manifest loaded" (result |> Option.is_some) true;
743- let manifest =
744- manifest |> Manifest.create ~path:"/fuu/bar" ~description:"" ~tags:[]
745- in
746+ let manifest = manifest |> Manifest.create ~path:"/fuu/bar" in
747 let result = manifest |> Manifest.find ~path:"/fuu/bar" in
748 Alcotest.(check bool)
749 "manifest /fuu/bar inserted" (result |> Option.is_some) true;
750 let result_path = (Option.value_exn result).path in
751 Alcotest.(check string) "result path" "/fuu/bar" result_path;
752- let manifest =
753- manifest |> Manifest.create ~path:"/fuu/baz" ~description:"" ~tags:[]
754- in
755+ let manifest = manifest |> Manifest.create ~path:"/fuu/baz" in
756 let results = manifest |> Manifest.list ~path:"/fuu" in
757 Alcotest.(check int) "n_results" 2 (List.length results);
758- let manifest =
759- manifest |> Manifest.create ~path:"/fuu/bar/qux" ~description:"" ~tags:[]
760- in
761+ let manifest = manifest |> Manifest.create ~path:"/fuu/bar/qux" in
762 let results = manifest |> Manifest.list ~path:"/fuu/bar" in
763 Alcotest.(check int) "n_results" 1 (List.length results);
764 Alcotest.(check int) "n_items" 4 (List.length manifest.items);
765 @@ -44,21 +43,24 @@ let test_manifest () =
766 print_endline (Manifest.to_string manifest);
767 Alcotest.(check int) "remove" 3 (List.length manifest.items)
768
769- let test_update () =
770+ let test_move () =
771 let manifest =
772 Manifest.make (Filename.temp_dir "note-test" "")
773- |> Manifest.create ~path:"/a" ~description:"" ~tags:[]
774- |> Manifest.create ~path:"/a/b" ~description:"" ~tags:[]
775+ |> Manifest.create ~path:"/a"
776+ |> Manifest.create ~path:"/a/b"
777 in
778 Alcotest.(check int) "two entries" 2 (List.length manifest.items);
779- let manifest =
780- manifest |> Manifest.update ~path:"/a/b" ~description:"" ~tags:[ "a"; "b" ]
781- in
782- let result = Option.value_exn (manifest |> Manifest.find ~path:"/a/b") in
783- Alcotest.(check string) "updated" "a" (List.nth_exn result.tags 0);
784- Alcotest.(check string) "updated" "b" (List.nth_exn result.tags 1);
785-
786- Alcotest.(check int) "two entries" 2 (List.length manifest.items)
787+ Alcotest.(check bool)
788+ "exists" true
789+ (Option.is_some (manifest |> Manifest.find ~path:"/a/b"));
790+ let manifest = manifest |> Manifest.move ~source:"/a/b" ~dest:"/b" in
791+ Alcotest.(check int) "two entries" 2 (List.length manifest.items);
792+ Alcotest.(check bool)
793+ "exists" true
794+ (Option.is_some (manifest |> Manifest.find ~path:"/b"));
795+ Alcotest.(check bool)
796+ "exists" false
797+ (Option.is_some (manifest |> Manifest.find ~path:"/a/b"))
798
799 let () =
800 Alcotest.run "Manifest"
801 @@ -70,7 +72,5 @@ let () =
802 ] );
803 ( "manifest",
804 [ Alcotest.test_case "test basic manifest" `Quick test_manifest ] );
805- ( "update",
806- [ Alcotest.test_case "test manifest update / move" `Quick test_update ]
807- );
808+ ("update", [ Alcotest.test_case "test manifest move" `Quick test_move ]);
809 ]
810 diff --git a/test/note_test.ml b/test/note_test.ml
811index 0034b5e..8fa6d3d 100644
812--- a/test/note_test.ml
813+++ b/test/note_test.ml
814 @@ -1,299 +1,60 @@
815 open Core
816 open Note_lib
817
818- let dump_notes (notes : Note.note list) =
819- List.iter ~f:(fun note -> print_endline note.frontmatter.title) notes
820-
821- let rec convert_tree tree =
822- let (Note.Tree (note, others)) = tree in
823- let title = note.frontmatter.title in
824- let title = "[" ^ title ^ "]" in
825- Display.Hierarchical.Tree (title, List.map ~f:convert_tree others)
826-
827- let empty () =
828- let _ = Note.of_string {||} in
829- Alcotest.(check pass) "just an empty note" "" ""
830-
831- let make_a_note () =
832- let note =
833+ let load_manifest () =
834+ let state_dir = Filename.temp_dir "note-test" "" in
835+ let manifest = Manifest.load_or_init state_dir in
836+ let note_root =
837+ Note.of_string {|
838+ ---
839+ path: "/"
840+ description: "all notes desend from here"
841+ tags: []
842+ ---
843+ # Root Note
844+ |} in
845+ let note_0 =
846 Note.of_string
847 {|
848 ---
849- title: this is a note
850- description: although it doesn't contain anything of value
851- tags: ["it", "is", "still", "a", "note"]
852+ path: "/note-0"
853+ description: "this is a note"
854+ tags: ["fuu", "bar"]
855 ---
856-
857- # What is the Purpose of Life?
858-
859- ```json
860- {
861- "answer": "it isn't clear"
862- }
863- ```
864- |}
865- in
866-
867- let title = note.frontmatter.title in
868- Alcotest.(check string) "title" "this is a note" title;
869- let description = note.frontmatter.description in
870- Alcotest.(check string)
871- "description" "although it doesn't contain anything of value" description;
872- let tags = note.frontmatter.tags in
873- Alcotest.(check (list string))
874- "tags"
875- [ "it"; "is"; "still"; "a"; "note" ]
876- tags;
877- let data =
878- Ezjsonm.find (note |> Note.to_json) [ "data" ]
879- |> Ezjsonm.get_list (fun x -> x)
880- |> List.hd_exn
881+ |}
882 in
883- let answer = Ezjsonm.find data [ "answer" ] |> Ezjsonm.get_string in
884- Alcotest.(check string) "answer" "it isn't clear" answer
885-
886- let note_of_title title =
887- let template =
888- sprintf {|
889+ let note_1 =
890+ Note.of_string
891+ {|
892 ---
893- title: %s
894+ path: "/note-0/note-1"
895+ description: "this is another note"
896+ tags: ["baz", "qux"]
897 ---
898-
899- # Note %s
900-
901- Hello World |} title title
902- in
903- Note.of_string template
904-
905- (*
906- [root]
907- ├──[note-0]
908- ├──[note-1]
909- │  └──[note-2]
910- ├──[note-3]
911- ├──[note-4]
912- │  ├──[note-5]
913- │  │  └──[note-6]
914- │  └──[note-7]
915- └──[note-8]
916- *)
917-
918- let tree =
919- Note.Tree
920- ( note_of_title "root",
921- [
922- Note.Tree (note_of_title "note-0", []);
923- Note.Tree
924- (note_of_title "note-1", [ Note.Tree (note_of_title "note-2", []) ]);
925- Note.Tree (note_of_title "note-3", []);
926- Note.Tree
927- ( note_of_title "note-4",
928- [
929- Note.Tree
930- ( note_of_title "note-5",
931- [ Note.Tree (note_of_title "note-6", []) ] );
932- Note.Tree (note_of_title "note-7", []);
933- ] );
934- Note.Tree (note_of_title "note-8", []);
935- ] )
936-
937- let find_many () =
938- let results =
939- Note.find_many
940- ~term:{ title = [ "note-3"; "note-4" ]; description = []; tags = [] }
941- ~notes:[] tree
942- in
943- dump_notes results;
944- let n_results = results |> List.length in
945- Alcotest.(check int) "two results" 2 n_results;
946- let n3 = List.nth_exn results 1 in
947- Alcotest.(check string) "first result" "note-3" n3.frontmatter.title;
948- let n4 = List.nth_exn results 0 in
949- Alcotest.(check string) "first result" "note-4" n4.frontmatter.title
950-
951- let insert_at () =
952- let n9 = note_of_title "note-9" in
953- let tree, inserted =
954- Note.insert
955- ~term:(Some { title = [ "note-3" ]; description = []; tags = [] })
956- ~tree
957- (Note.Tree (n9, []))
958- in
959- Alcotest.(check bool) "inserted" true inserted;
960- let result =
961- Note.find_one
962- ~term:{ title = [ "note-9" ]; description = []; tags = [] }
963- tree
964- in
965- Alcotest.(check bool) "inserted" true (Option.is_some result)
966-
967- let test_structure () =
968- let expected =
969- {|
970- [root]
971- ├──[note-0]
972- ├──[note-1]
973- │ └──[note-2]
974- ├──[note-3]
975- ├──[note-4]
976- │ ├──[note-5]
977- │ │ └──[note-6]
978- │ └──[note-7]
979- └──[note-8]
980- |}
981- in
982- Alcotest.(check int) "length" 9 (Note.length tree);
983- let note_tree = tree |> convert_tree |> Display.Hierarchical.to_string in
984- Alcotest.(check string) "structure" expected note_tree
985-
986- (*
987- we are attempting to go from a flat list of things that may or may
988- not reference other items from within the list and construct a hiarchrial
989- tree.
990-
991- Example:
992-
993- [
994- note-1
995- note-2 [ref note-4]
996- note-3
997- note-4 [ref note-1]
998- note-5
999- ]
1000-
1001- .
1002- ├──[note-1]
1003- │ │──[note-4]
1004- │ └──[note-2]
1005- │note-3
1006- └note-5
1007-
1008-
1009- def resolve(tree, notes):
1010- buf = []
1011- for note in notes:
1012- if note.ref == None:
1013- tree.add(note)
1014- else:
1015- inserted = tree.insert(note.ref)
1016- if not inserted:
1017- buf.append(note)
1018- if buf.length > 0:
1019- return resolve(tree, buf)
1020- return tree
1021-
1022- *)
1023- let n0 = Note.of_string {|
1024- ---
1025- title: note-0
1026- ---
1027- # Note 0
1028- |}
1029-
1030- let n1 =
1031- Note.of_string
1032- {|
1033- ---
1034- title: note-1
1035- parent: {"title": ["note-0"]}
1036- ---
1037- # Note 1
1038- |}
1039-
1040- let n2 = Note.of_string {|
1041- ---
1042- title: note-2
1043- ---
1044- # Note 2
1045- |}
1046-
1047- let n3 =
1048- Note.of_string
1049- {|
1050- ---
1051- title: note-3
1052- parent: {"title": ["note-1"]}
1053- ---
1054- # Note 3
1055- |}
1056-
1057- let n4 =
1058- Note.of_string
1059- {|
1060- ---
1061- title: note-4
1062- parent: {"title": ["note-1"]}
1063- ---
1064- # Note 4
1065- |}
1066-
1067- let test_buf_insert () =
1068- let root = Note.Tree (Note.of_string Note.root_template, []) in
1069- let tree, buf = Note.buf_insert ~root [ n3; n2; n1; n0 ] in
1070- Alcotest.(check int) "n" 2 (List.length buf);
1071- let _, buf = Note.buf_insert ~root:tree buf in
1072- Alcotest.(check int) "n" 0 (List.length buf)
1073-
1074- let test_resolve () =
1075- let root = Note.Tree (Note.of_string Note.root_template, []) in
1076- let expected =
1077- {|
1078- [root]
1079- ├──[note-2]
1080- └──[note-0]
1081- └──[note-1]
1082- ├──[note-4]
1083- └──[note-3]
1084- |}
1085- in
1086- let tree_as_string =
1087- [ n3; n2; n1; n0; n4 ] |> Note.resolve ~root |> convert_tree
1088- |> Display.Hierarchical.to_string
1089- in
1090- Alcotest.(check string) "resolve" expected tree_as_string
1091-
1092- let test_resolve_manifest () =
1093-
1094- (*
1095- let expected =
1096- {|
1097- [root]
1098- └──[n0]
1099- └──[n1]
1100- └──[n2]
1101 |}
1102 in
1103- let tree = Note.Tree (Note.of_string Note.root_template, []) in
1104- let tree_as_string =
1105- manifest
1106- |> Note.resolve_manifest ~path:"/" ~tree
1107- |> convert_tree |> Display.Hierarchical.to_string
1108- in *)
1109- Alcotest.(check pass) "resolve-manifest" "asfd" ",,,,"
1110+ let manifest = manifest |> Manifest.create ~path:"/" in
1111+ let item = manifest.items |> List.hd_exn in
1112+ let slug = item.slug |> Slug.to_string in
1113+ Io.create ~callback:None ~content:(note_root |> Note.to_string) slug ;
1114+ let manifest = manifest |> Manifest.create ~path:"/note-0" in
1115+ let item = manifest.items |> List.hd_exn in
1116+ let slug = item.slug |> Slug.to_string in
1117+ Io.create ~callback:None ~content:(note_0 |> Note.to_string) slug ;
1118+ manifest |> Manifest.save ;
1119+ let manifest = manifest |> Manifest.create ~path:"/note-0/note-1" in
1120+ let item = manifest.items |> List.hd_exn in
1121+ let slug = item.slug |> Slug.to_string in
1122+ Io.create ~callback:None ~content:(note_1 |> Note.to_string) slug ;
1123+ manifest |> Manifest.save ;
1124+ let manifest = Manifest.load_or_init state_dir in
1125+ let root = (Note.Tree ((Note.of_string ~path:(Some "/") Note.root_template), (Note.resolve_manifest ~path:"/" manifest))) in
1126+ let (Note.Tree (_, others)) = root in
1127+ Alcotest.(check int) "one" 1 (others |> List.length)
1128
1129 let () =
1130 Alcotest.run "Note"
1131 [
1132- ( "empty",
1133- [ Alcotest.test_case "parse an empty note" `Quick empty ]
1134- );
1135- ( "create",
1136- [ Alcotest.test_case "create a note from a string" `Quick make_a_note ]
1137- );
1138- ( "find-many",
1139- [
1140- Alcotest.test_case "find notes with multiple criteria" `Quick
1141- find_many;
1142- ] );
1143- ( "insert",
1144- [ Alcotest.test_case "insert a note into a tree" `Quick insert_at ] );
1145- ( "structure",
1146- [ Alcotest.test_case "note tree structure" `Quick test_structure ] );
1147- ( "buf_insert",
1148- [ Alcotest.test_case "buf insert flat list" `Quick test_buf_insert ] );
1149- ("resolve", [ Alcotest.test_case "resolve flat list" `Quick test_resolve ]);
1150- ( "resolve-manifest",
1151- [
1152- Alcotest.test_case "resolve via manifest" `Quick test_resolve_manifest;
1153- ] );
1154+ ( "load_manifest",
1155+ [ Alcotest.test_case "load manifest" `Quick load_manifest ] );
1156 ]