From: Alexander Goussas Date: Sat, 18 Apr 2026 16:06:03 +0000 (-0500) Subject: implement template replacement X-Git-Tag: v0.0.1~7 X-Git-Url: http://git.frustrated-labs.net/?a=commitdiff_plain;h=a41ecaa2c45aa02aaf85597d4fbd002c8cf840a2;p=frustrated-functor.dev.git implement template replacement --- diff --git a/bin/blog-processor/src/html_formatter.zig b/bin/blog-processor/src/html_formatter.zig index 71b0384..99644ba 100644 --- a/bin/blog-processor/src/html_formatter.zig +++ b/bin/blog-processor/src/html_formatter.zig @@ -1,5 +1,6 @@ const std = @import("std"); const md = @import("./markdown_parser.zig"); +const strings = @import("./string_utils.zig"); const stdC = @cImport({ @cInclude("stdio.h"); @@ -42,6 +43,9 @@ const HtmlFormatter = struct { } if (self.opts.template) |t| { + const replaced = try strings.replace(alloc, t, "{body}", self.buffer.items); + self.buffer.deinit(alloc); + self.buffer = std.ArrayList(u8).fromOwnedSlice(replaced); } return self.buffer.items; @@ -154,7 +158,7 @@ test "can format document with more than one element" { test "can format empty document with template" { const alloc = std.testing.allocator; const template = - \\%s + \\{body} ; var formatter = HtmlFormatter.init(.{ .template = template }); defer formatter.deinit(alloc); @@ -169,7 +173,7 @@ test "can format empty document with template" { test "can format empty non-document with template" { const alloc = std.testing.allocator; const template = - \\%s + \\{body} ; var formatter = HtmlFormatter.init(.{ .template = template }); defer formatter.deinit(alloc); diff --git a/bin/blog-processor/src/root.zig b/bin/blog-processor/src/root.zig index 72e7c52..49eabb0 100644 --- a/bin/blog-processor/src/root.zig +++ b/bin/blog-processor/src/root.zig @@ -6,4 +6,5 @@ test "all tests" { _ = @import("./markdown_parser.zig"); _ = @import("./html_formatter.zig"); + _ = @import("./string_utils.zig"); } diff --git a/bin/blog-processor/src/string_utils.zig b/bin/blog-processor/src/string_utils.zig new file mode 100644 index 0000000..70a2c15 --- /dev/null +++ b/bin/blog-processor/src/string_utils.zig @@ -0,0 +1,64 @@ +const std = @import("std"); + +/// Replace target in s by replacement. +/// Return an owned buffer. +pub fn replace(alloc: std.mem.Allocator, s: []const u8, target: []const u8, replacement: []const u8) ![]u8 { + var buffer = try std.ArrayList(u8).initCapacity(alloc, s.len); + + var i: usize = 0; + while (i < s.len) { + var j: usize = i; + const matched = while (j < s.len and j - i < target.len) { + if (s[j] != target[j - i]) break false; + j += 1; + } else true; + if (matched) { + try buffer.appendSlice(alloc, replacement); + i = j; + } else { + try buffer.append(alloc, s[i]); + i += 1; + } + } + + return try buffer.toOwnedSlice(alloc); +} + +test "replace can replace single instance of target in string" { + var alloc = std.testing.allocator; + + const s = "This is a {target}"; + const target = "{target}"; + const replacement = "test"; + + const result = try replace(alloc, s, target, replacement); + defer alloc.free(result); + + try std.testing.expectEqualStrings("This is a test", result); +} + +test "replace can replace multiple instances of target in string" { + var alloc = std.testing.allocator; + + const s = "This is a {target} to {target} {target}s"; + const target = "{target}"; + const replacement = "test"; + + const result = try replace(alloc, s, target, replacement); + defer alloc.free(result); + + try std.testing.expectEqualStrings("This is a test to test tests", result); +} + +test "replace when replacing target with target is a no-op" { + var alloc = std.testing.allocator; + + const s = "This is a {target} to {target} {target}s"; + const target = "{target}"; + const replacement = "{target}"; + + const result = try replace(alloc, s, target, replacement); + defer alloc.free(result); + + try std.testing.expectEqualStrings(s, result); +}