From: Alexander Goussas Date: Sun, 12 Apr 2026 17:23:13 +0000 (-0500) Subject: feat: implement parsing frontmatter X-Git-Tag: v0.0.1~13 X-Git-Url: http://git.frustrated-labs.net/?a=commitdiff_plain;h=6ff98eea7b47c5106f9d5be9850e28897b8b097c;p=frustrated-functor.dev.git feat: implement parsing frontmatter --- diff --git a/bin/blog-processor/src/markdown_parser.zig b/bin/blog-processor/src/markdown_parser.zig index 42dfaa5..e909dba 100644 --- a/bin/blog-processor/src/markdown_parser.zig +++ b/bin/blog-processor/src/markdown_parser.zig @@ -1,31 +1,135 @@ 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, ¤t), - _ => current += 1, + '-' => try parse_frontmatter(¤t, 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); +} diff --git a/bin/blog-processor/src/root.zig b/bin/blog-processor/src/root.zig index 7d34b61..5b7b6ef 100644 --- a/bin/blog-processor/src/root.zig +++ b/bin/blog-processor/src/root.zig @@ -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"); +}