Commit
+57 -44 +/-2 browse
1 | diff --git a/lib/slug.ml b/lib/slug.ml |
2 | index 9a2bd33..814f61d 100644 |
3 | --- a/lib/slug.ml |
4 | +++ b/lib/slug.ml |
5 | @@ -1,7 +1,18 @@ |
6 | open Core |
7 | |
8 | + let pattern = Re.Pcre.regexp {|(.*)?note-(\d{8})-(\d{1,})(.md)?|} |
9 | + |
10 | type t = { path : string; date : Date.t; index : int } |
11 | |
12 | + let to_string slug = slug.path |
13 | + |
14 | + let of_string path = |
15 | + let result = Re.all pattern path |> List.hd_exn in |
16 | + let items = Re.Group.all result |> Array.to_list in |
17 | + let date = Date.parse ~fmt:"%Y%m%d" (List.nth_exn items 2) in |
18 | + let index = int_of_string (List.nth_exn items 3) in |
19 | + { path; date; index } |
20 | + |
21 | let shortname t = |
22 | let date_str = Date.format t.date "%Y%m%d" in |
23 | sprintf "note-%s-%d" date_str t.index |
24 | @@ -11,37 +22,32 @@ let compare s1 s2 = String.compare s1.path s2.path |
25 | let is_note path = |
26 | Filename.basename path |> String.is_substring ~substring:"note-" |
27 | |
28 | - let of_path path = |
29 | - let slug = Filename.chop_extension (Filename.basename path) in |
30 | - let split = String.split ~on:'-' slug in |
31 | - let date = Date.parse ~fmt:"%Y%m%d" (List.nth_exn split 1) in |
32 | - let index = int_of_string (List.nth_exn split 2) in |
33 | - { path; date; index } |
34 | - |
35 | let load state_dir = |
36 | state_dir |> Sys.ls_dir |> List.filter ~f:is_note |
37 | |> List.map ~f:(Filename.concat state_dir) |
38 | - |> List.map ~f:of_path |
39 | - |
40 | - let next state_dir = |
41 | - let slugs = load state_dir in |
42 | - (* find all slugs for today (00:00:00 -> 23:59:59) *) |
43 | - let now = Time.now () in |
44 | - let today = Time.to_date ~zone:Time.Zone.utc now in |
45 | - let tomorrow = Date.add_days today 1 in |
46 | - let filtered = |
47 | - List.filter |
48 | - ~f:(fun slug -> Date.between ~low:today ~high:tomorrow slug.date) |
49 | - slugs |
50 | - in |
51 | - let next_int = |
52 | - List.fold ~init:(-1) |
53 | - ~f:(fun accm slug -> if slug.index > accm then slug.index else accm) |
54 | - filtered |
55 | - + 1 |
56 | - in |
57 | - let date_str = Date.format today "%Y%m%d" in |
58 | - let path = |
59 | - Filename.concat state_dir (Core.sprintf "note-%s-%d.md" date_str next_int) |
60 | - in |
61 | - { path; date = today; index = next_int } |
62 | + |> List.map ~f:of_string |
63 | + |
64 | + let next ?(last = None) state_dir = |
65 | + let today = Time.now () |> Time.to_date ~zone:Time.Zone.utc in |
66 | + let today_str = Date.format today "%Y%m%d" in |
67 | + match last with |
68 | + | Some last -> |
69 | + let tomorrow = Date.add_days today 1 in |
70 | + if Date.between ~low:today ~high:tomorrow last.date then |
71 | + let index = last.index + 1 in |
72 | + let path = |
73 | + Filename.concat state_dir (sprintf "note-%s-%d.md" today_str index) |
74 | + in |
75 | + { path; date = today; index } |
76 | + else |
77 | + let index = 0 in |
78 | + let path = |
79 | + Filename.concat state_dir (sprintf "note-%s-%d.md" today_str index) |
80 | + in |
81 | + { path; date = today; index } |
82 | + | None -> |
83 | + let index = 0 in |
84 | + let path = |
85 | + Filename.concat state_dir (Core.sprintf "note-%s-%d.md" today_str index) |
86 | + in |
87 | + { path; date = today; index } |
88 | diff --git a/test/slug_test.ml b/test/slug_test.ml |
89 | index c02fa90..f4af219 100644 |
90 | --- a/test/slug_test.ml |
91 | +++ b/test/slug_test.ml |
92 | @@ -1,14 +1,22 @@ |
93 | open Core |
94 | open Note_lib |
95 | |
96 | + let test_slug_of_string () = |
97 | + let input = "/fuu/bar/note-19700101-0.md" in |
98 | + let slug = input |> Slug.of_string in |
99 | + Alcotest.(check string) "conversion" input (slug |> Slug.to_string); |
100 | + let input = "note-19700101-0" in |
101 | + let slug = input |> Slug.of_string in |
102 | + Alcotest.(check string) "bare" input (slug |> Slug.to_string) |
103 | + |
104 | let test_slug_shortname () = |
105 | - let s1 = Slug.of_path "fuu/note-19700101-0.md" in |
106 | + let s1 = Slug.of_string "fuu/note-19700101-0.md" in |
107 | Alcotest.(check string) "short name" "note-19700101-0" (Slug.shortname s1) |
108 | |
109 | let test_slug_comparable () = |
110 | let s1, s2 = |
111 | - ( Slug.of_path "fuu/note-19700101-0.md", |
112 | - Slug.of_path "fuu/note-19700101-0.md" ) |
113 | + ( Slug.of_string "fuu/note-19700101-0.md", |
114 | + Slug.of_string "fuu/note-19700101-0.md" ) |
115 | in |
116 | Alcotest.(check bool) "identical paths" true (Slug.compare s1 s2 = 0) |
117 | |
118 | @@ -17,25 +25,24 @@ let test_slug_path () = |
119 | let state_dir = Filename.temp_dir "note-test" "" in |
120 | let slug = Slug.next state_dir in |
121 | let expected = |
122 | - Filename.concat state_dir (sprintf "note-%s-0.md" date_string) |
123 | + Filename.concat state_dir (sprintf "note-%s-0.md" date_string) |
124 | in |
125 | Alcotest.(check string) "path" expected slug.path |
126 | |
127 | let test_slug_increment () = |
128 | - let state_dir = Filename.temp_dir "note-test" "" in |
129 | let date_string = Time.format (Time.now ()) "%Y%m%d" ~zone:Time.Zone.utc in |
130 | - for i = 0 to 5 do |
131 | - let filename = |
132 | - Filename.concat state_dir (sprintf "note-%s-%d.md" date_string i) |
133 | - in |
134 | - Out_channel.write_all filename ~data:"" |
135 | - done; |
136 | - let slug = Slug.next state_dir in |
137 | - Alcotest.(check int) "index" 6 slug.index |
138 | + let slug = Slug.next "/fuu/bar" in |
139 | + let expected = sprintf "/fuu/bar/note-%s-%d.md" date_string 0 in |
140 | + Alcotest.(check string) "check path" expected (slug |> Slug.to_string); |
141 | + let slug = Slug.next ~last:(Some slug) "/fuu/bar" in |
142 | + let expected = sprintf "/fuu/bar/note-%s-%d.md" date_string 1 in |
143 | + Alcotest.(check string) "check path" expected (slug |> Slug.to_string) |
144 | |
145 | let () = |
146 | Alcotest.run "Slug" |
147 | [ |
148 | + ( "slug-of-string", |
149 | + [ Alcotest.test_case "ofstring" `Quick test_slug_of_string ] ); |
150 | ( "slug-comparable", |
151 | [ Alcotest.test_case "compare" `Quick test_slug_comparable ] ); |
152 | ( "slug-shortname", |