]> git.frustrated-labs.net Git - frustrated-functor.dev.git/commitdiff
feat: implement parsing frontmatter
authorAlexander Goussas <[email protected]>
Sun, 12 Apr 2026 17:23:13 +0000 (12:23 -0500)
committerAlexander Goussas <[email protected]>
Sun, 12 Apr 2026 17:23:13 +0000 (12:23 -0500)
bin/blog-processor/src/markdown_parser.zig
bin/blog-processor/src/root.zig

index 42dfaa5359e7251473fd63c7783efea437d22ee5..e909dba4d10a9f59457ab035e2e11b8ff7fd3f06 100644 (file)
 const std = @import("std");
+const assert = std.debug.assert;
+
+const logger = std.log.scoped(.markdown_parser);
+
+const MarkdownParserError = error{
+    UnexpectedToken,
+};
 
 pub const MarkdownDoc = struct {
     date: []const u8,
     summary: []const u8,
     content: []const u8,
 
-    pub fn parse(doc: []const u8, alloc: std.mem.Allocator) @This() {
+    pub fn parse(doc: []const u8, alloc: std.mem.Allocator) MarkdownParserError!@This() {
         _ = alloc;
 
-        var start = 0;
-        var current = 0;
+        var start: usize = 0;
+        var current: usize = 0;
+
+        var date: []const u8 = undefined;
+        var summary: []const u8 = undefined;
 
         while (current < doc.len) {
             start = current;
             const c = doc[current];
             switch (c) {
-                '-' => parse_frontmatter(&start, &current),
-                _ => current += 1,
+                '-' => try parse_frontmatter(&current, doc, &date, &summary),
+                else => current += 1,
             }
         }
 
-        return MarkdownDoc{};
+        return .{
+            .date = date,
+            .summary = summary,
+            .content = undefined
+        };
     }
 
     fn parse_frontmatter(
-        start: *u32,
-        current: *u32
-    ) void {
+        current: *usize,
+        doc: []const u8,
+        date: *[]const u8, 
+        summary: *[]const u8,
+    ) MarkdownParserError!void {
+        assert(doc[current.*] == '-');
+
+        advanceWhile(doc, current, '-');
+        try expectToken(doc, '\n', current);
+
+        try parse_frontmatter_value(current, doc, date);
+        try parse_frontmatter_value(current, doc, summary);
+
+        try expectToken(doc, '-', current);
+        advanceWhile(doc, current, '-');
+    }
+
+    fn parse_frontmatter_value(current: *usize, doc: []const u8, value: *[]const u8) MarkdownParserError!void {
+        var start: usize = undefined;
+        
+        advanceWhileFn(doc, current, std.ascii.isAlphabetic);
+
+        try expectToken(doc, ':', current);
+
+        advanceWhile(doc, current, ' ');
+        start = current.*;
+
+        advanceWhileNot(doc, current, '\n');
+        try expectToken(doc, '\n', current);
+
+        value.* = doc[start..current.* - 1];
+    }
+
+    fn expectToken(doc: []const u8, c: u8, current: *usize) MarkdownParserError!void {
+        if (doc[current.*] != c) {
+            const errMsg = "Expected '" ++ [1]u8{c} ++ "', but got '" ++ [1]u8{doc[current.*]} ++ "'";
+            if (!@inComptime()) {
+                logger.err(errMsg, .{});
+                return error.UnexpectedToken;
+            } else {
+                @compileError(errMsg);
+            }
+        }
+        assert(doc[current.*] == c);
+        current.* += 1;
+    }
+
+    fn advanceWhileFn(doc: []const u8, current: *usize, f: fn(u8) bool) void {
+        while (current.* < doc.len and f(doc[current.*])) {
+            current.* += 1;
+        }
+    }
+
+    fn advanceWhile(doc: []const u8, current: *usize, c: u8) void {
+        while (current.* < doc.len and doc[current.*] == c) {
+            current.* += 1;
+        }
+    }
+
+    fn advanceWhileNot(doc: []const u8, current: *usize, c: u8) void {
+        while (current.* < doc.len and doc[current.*] != c) {
+            current.* += 1;
+        }
     }
 };
+
+test "can parse date in frontmatter" {
+    const doc = 
+        \\----
+        \\date: 12/04/2026
+        \\summary: This is the shit!
+        \\----
+        ;
+
+    const alloc = std.testing.allocator;
+
+    const result = comptime MarkdownDoc.parse(doc, alloc) catch unreachable;
+
+    try std.testing.expectEqualStrings("12/04/2026", result.date);
+}
+
+test "can parse summary in frontmatter" {
+    const doc = 
+        \\----
+        \\date: 12/04/2026
+        \\summary: This is the shit!
+        \\----
+        ;
+
+    const alloc = std.testing.allocator;
+
+    const result = comptime MarkdownDoc.parse(doc, alloc) catch unreachable;
+
+    try std.testing.expectEqualStrings("This is the shit!", result.summary);
+}
index 7d34b613de40b558e3796c334267dddba2072837..5b7b6efbd228f365df4c120a383c8395d09493ba 100644 (file)
@@ -9,3 +9,7 @@ pub fn parse_markdown(md: []const u8, alloc: std.mem.Allocator) std.ArrayList(En
     _ = md;
     _ =  unreachable;
 }
+
+test "all tests" {
+    _ = @import("./markdown_parser.zig");
+}