Logo

index : blog

---

  • summary
  • about
  • tree
  • log
  • branches
<< path: root/public/blog.git/html/src/gen/update.jai blob: 0ada8732ed1ed6194477b0f86ebcc6d74413dde7 [raw] [clear marker]

        
0
1
2
3update_metadata_in_posts :: (posts: [..]Markdown_File) {
4 re_pattern_datetime=, ok := re.compile(PATTERN_RE_DATETIME);
5 assert(ok);
6
7 posts_updated: [..]string;
8
9 for posts {
10 metadata, post := entries_make_header_and_body(it.content);
11 updated_metadata := update_metadata(metadata);
12 joined := join_metadata_and_body(updated_metadata, post);
13 array_add(*posts_updated, joined);
14 }
15
16 assert(posts_updated.count == posts.count, "This should not happen!");
17
18 for posts_updated {
19 fp := posts[it_index].fn;
20 file_write_or_exit(fp, it);
21 log("> Updated: %", fp);
22 }
23}
24
25is_correct_field_date_format :: (value: string) -> bool {
26 matched, captures := re.match(value, re_pattern_datetime);
27 this_allocation_is_not_a_leak(captures.data);
28 return matched;
29}
30
31
32#scope_file
33
34
35re_pattern_datetime: re.Regexp;
36
37
38Input_Kind :: enum {
39 EMPTY;
40 MS_MAYBE;
41 DATE_TIME;
42 DATE_ONLY;
43 DT_NOW;
44 ALREADY_PARSED;
45}
46
47/** Note: If there are multiple occurrences of 'published' or 'updated'
48 it will use the last found item.
49*/
50update_metadata :: (metadata: string) -> string {
51 metadata_trimmed := trim(metadata);
52 meta := split(metadata_trimmed, "\n");
53
54 published_count := count_occurrences_in_field(meta, PATTERN_PUBLISHED);
55 updated_count := count_occurrences_in_field(meta, PATTERN_UPDATED);
56
57 if published_count > 1
58 then log_metadata_error_and_exit(metadata, PATTERN_PUBLISHED, published_count);
59
60 if updated_count> 1
61 then log_metadata_error_and_exit(metadata, PATTERN_UPDATED, updated_count);
62
63
64 buf: [..]string;
65
66 for meta {
67 normalized := trim(it);
68
69 if starts_with(normalized, PATTERN_PUBLISHED) {
70 add_time_to_array(*buf, normalized, PATTERN_PUBLISHED);
71 }
72 else if starts_with(normalized, PATTERN_UPDATED) {
73 add_time_to_array(*buf, normalized, PATTERN_UPDATED);
74 }
75 else {
76 array_add(*buf, normalized);
77 }
78 }
79
80 if !published_count then add_current_date_and_ms(*buf, PATTERN_PUBLISHED);
81 if !updated_count then add_current_date_and_ms(*buf, PATTERN_UPDATED);
82
83 s := join(..buf, "\n", after_last=true,, temp);
84 return copy_string(s);
85}
86
87log_metadata_error_and_exit :: (metadata: string, pattern: string, counter: int) {
88 log_error(
89 "Field pattern `%` was found % time(s) in the following header:",
90 pattern,
91 counter
92 );
93 log_error("-- Metadata Preview -----------\n%", slice(metadata, 0, 255));
94 exit(1);
95}
96
97count_occurrences_in_field :: (metadata: []string, pattern: string) -> count: int {
98 counter: int;
99
100 for metadata {
101 trimmed := trim(it);
102 if starts_with(trimmed, pattern) then counter += 1;
103 }
104
105 return counter;
106}
107
108join_metadata_and_body :: (metadata: string, body: string) -> string {
109 return join(metadata, METADATA_MARKER, "\n\n", body);
110}
111
112make_metadata_field :: (pattern: string, value: string, ms: string) -> string {
113 return tprint("%%(%)", pattern, value, ms);
114}
115
116add_time_to_array :: (buf: *[..]string, value: string, $pattern: string) {
117 if is_correct_field_date_format(value) {
118 array_add(buf, value);
119 return;
120 }
121
122 value, ms := convert_input_to_ms(value, pattern.count);
123 metadata_field := make_metadata_field(pattern, value, ms);
124 array_add(buf, metadata_field);
125}
126
127convert_input_to_ms :: (
128 source: string, pattern_length: int
129)
130 -> (value: string, ms: string)
131{
132 ms: int;
133 value: string;
134
135 sliced := slice(source, pattern_length, source.count);
136 trimmed := trim(sliced);
137
138 input_kind := determine_input_kind(trimmed);
139
140 if #complete input_kind == {
141 case .EMPTY;
142 ms = current_time_as_ms();
143 value = milliseconds_to_date(ms);
144 case .MS_MAYBE;
145 ms = parse_to_int(trimmed);
146 value = milliseconds_to_date(ms);
147 case .DATE_TIME;
148 ms = date_to_milliseconds(trimmed);
149 value = trimmed;
150 case .DATE_ONLY;
151 ms = date_to_milliseconds(trimmed);
152 value = trimmed;
153 case .DT_NOW;
154 ms = current_time_as_ms();
155 value = milliseconds_to_date(ms);
156 case .ALREADY_PARSED;
157 return "", "";
158 }
159
160 ms_as_string := tprint("%", ms);
161 return value, ms_as_string;
162}
163
164add_current_date_and_ms :: (buf: *[..]string, pattern: string) {
165 ms := current_time_as_ms();
166 ms_as_string := tprint("%", ms);
167 value := milliseconds_to_date(ms);
168 metadata_field := make_metadata_field(pattern, value, ms_as_string);
169 array_add(buf, metadata_field);
170}
171
172determine_input_kind :: (value: string) -> (kind: Input_Kind = .EMPTY) {
173 kind: Input_Kind;
174
175 if !value || is_whole_thing_whitespace(value) {
176 return;
177 }
178 else if is_number_format(value) {
179 kind = .MS_MAYBE;
180 }
181 else if is_date_time_format(value, PARSER_FORMAT_DATETIME_DELIMITER_COUNT) {
182 kind = .DATE_TIME;
183 }
184 else if is_date_time_format(value, PARSER_FORMAT_DATE_DELIMITER_COUNT) {
185 kind = .DATE_ONLY;
186 }
187 else if is_current_dt_format(value) {
188 kind = .DT_NOW;
189 }
190 else {
191 assert(false, "You shouldn't be here!");
192 }
193
194 return kind;
195}
196
197current_time_as_ms :: () -> int {
198 now := current_time_consensus();
199 ms := to_milliseconds(now);
200 return ms;
201}
202
203date_to_milliseconds :: (s: string) -> int {
204 ct: Calendar_Time;
205 date := split(s, "-");
206
207 if date.count != 3 && date.count != 5 {
208 log_error("Date string must have 3 or 5 items");
209 exit(1);
210 }
211
212 for date if it_index == {
213 case 0; ct.year = parse_to_int(it, s32);
214 case 1; ct.month_starting_at_0 = parse_to_int(it, s8) - 1;
215 case 2; ct.day_of_month_starting_at_0 = parse_to_int(it, s8) - 1;
216 case 3; ct.hour = parse_to_int(it, s8);
217 case 4; ct.minute = parse_to_int(it, s8);
218 }
219
220 ct.time_zone = .UTC;
221
222 apollo := calendar_to_apollo(ct);
223 ms := to_milliseconds(apollo);
224
225 return ms;
226}
227
228milliseconds_to_date :: (ms: int) -> string {
229 a := milliseconds_to_apollo(ms);
230 cal := to_calendar(a, .UTC);
231
232 year := tprint("%", cal.year);
233 month := string_pad_left(
234 tprint("%", cal.month_starting_at_0 + 1),
235 2, "0"
236 );
237 day := string_pad_left(
238 tprint("%", cal.day_of_month_starting_at_0 + 1),
239 2, "0"
240 );
241
242 joined := join(year, month, day, separator="-");
243 return joined;
244}
245
246is_whole_thing_whitespace :: (s: string) -> bool {
247 for s if !is_space(it) return false;
248 return true;
249}
250
251is_number_format :: (s: string) -> bool {
252 for s if !is_digit(it) return false;
253 return true;
254}
255
256is_date_time_format :: (value: string, $delimiter_count: int) -> bool {
257 count_is: int;
258 for value if it == PARSER_FORMAT_DT_DELIMITER then count_is += 1;
259 return delimiter_count == count_is;
260}
261
262is_current_dt_format :: (value: string) -> bool {
263 return value == METADATA_CMD_CURRENT_DT;
264}
265
266
Copyright 2026  E766CB298A6D1E64 | Git-Thing heavily inspired by cgit