diff options
| author | Anhgelus Morhtuuzh <william@herges.fr> | 2026-04-16 22:33:44 +0200 |
|---|---|---|
| committer | Anhgelus Morhtuuzh <william@herges.fr> | 2026-04-16 22:33:44 +0200 |
| commit | 6df64050b1442a5f3a0f566cd816639ac1fd298f (patch) | |
| tree | 81de54495e09c501d7d2839828523eaabb7a0569 /src/dom | |
| parent | 11cc71f3b59fa62fd2fb2cb3b84e689317fb1268 (diff) | |
feat(dom): element generator
Diffstat (limited to 'src/dom')
| -rw-r--r-- | src/dom/Element.zig | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/src/dom/Element.zig b/src/dom/Element.zig new file mode 100644 index 0000000..ff8a3d1 --- /dev/null +++ b/src/dom/Element.zig @@ -0,0 +1,150 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const eql = std.mem.eql; + +pub const Kind = enum { + void, + content, + literal, +}; + +const Self = @This(); + +kind: Kind, +tag: ?[]const u8 = null, +attributes: std.StringArrayHashMap([]const u8), +class_list: std.BufSet, +content: ?[]*Self = null, +literal: ?[]const u8 = null, + +pub fn init(alloc: Allocator, knd: Kind, tag: []const u8) Self { + return .{ + .kind = knd, + .tag = tag, + .attributes = .init(alloc), + .class_list = .init(alloc), + }; +} + +pub fn initLit(alloc: Allocator, literal: []const u8) Self { + return .{ .kind = .literal, .literal = literal, .attributes = .init(alloc), .class_list = .init(alloc) }; +} + +pub fn deinit(self: *Self) void { + self.attributes.deinit(); + self.class_list.deinit(); +} + +pub fn render(self: *Self, alloc: Allocator) !std.ArrayList(u8) { + var attr = try self.renderAttribute(alloc); + defer attr.deinit(alloc); + var acc = try std.ArrayList(u8).initCapacity(alloc, 2); + errdefer acc.deinit(alloc); + if (self.tag) |tag| { + try acc.append(alloc, '<'); + try acc.appendSlice(alloc, tag); + try acc.appendSlice(alloc, attr.items); + try acc.append(alloc, '>'); + } + switch (self.kind) { + .void => return acc, + .content => { + if (self.content) |content| { + for (content) |it| { + var sub = try it.render(alloc); + defer sub.deinit(alloc); + try acc.appendSlice(alloc, sub.items); + } + } + }, + .literal => try acc.appendSlice(alloc, self.literal.?), + } + if (self.tag) |tag| { + try acc.appendSlice(alloc, "</"); + try acc.appendSlice(alloc, tag); + try acc.append(alloc, '>'); + } + return acc; +} + +fn renderAttribute(self: *Self, alloc: Allocator) !std.ArrayList(u8) { + var iter = self.attributes.iterator(); + if (iter.len == 0) return .empty; + var acc = try std.ArrayList(u8).initCapacity(alloc, 2); + errdefer acc.deinit(alloc); + try acc.append(alloc, ' '); + var i: usize = 0; + while (iter.next()) |it| : (i += 1) { + try acc.appendSlice(alloc, it.key_ptr.*); + try acc.appendSlice(alloc, "=\""); + // MISSING ESCAPING!!! + try acc.appendSlice(alloc, it.value_ptr.*); + try acc.append(alloc, '"'); + if (i < iter.len - 1) try acc.append(alloc, ' '); + } + return acc; +} + +pub fn setAttribute(self: *Self, k: []const u8, v: []const u8) !void { + try self.attributes.put(k, v); +} + +pub fn removeAttribute(self: *Self, k: []const u8) void { + _ = self.attributes.orderedRemove(k); +} + +pub fn hasAttribute(self: *Self, k: []const u8) bool { + return self.attributes.contains(k); +} + +fn doTest(alloc: Allocator, el: *Self, exp: []const u8) !void { + var rendered = try el.render(alloc); + defer rendered.deinit(alloc); + std.testing.expect(eql(u8, rendered.items, exp)) catch |err| { + std.debug.print("{s}\n", .{rendered.items}); + return err; + }; +} + +test "void element" { + var arena = std.heap.DebugAllocator(.{}).init; + defer _ = arena.deinit(); + const alloc = arena.allocator(); + + var br = init(alloc, .void, "br"); + defer br.deinit(); + + try doTest(alloc, &br, "<br>"); + + var img = init(alloc, .void, "img"); + defer img.deinit(); + try img.setAttribute("src", "foo"); + try img.setAttribute("alt", "bar"); + + try doTest(alloc, &img, "<img src=\"foo\" alt=\"bar\">"); +} + +test "content element" { + var arena = std.heap.DebugAllocator(.{}).init; + defer _ = arena.deinit(); + const alloc = arena.allocator(); + + var p = init(alloc, .content, "p"); + defer p.deinit(); + + var content = initLit(alloc, "hello world"); + defer content.deinit(); + var in = [_]*Self{&content}; + p.content = ∈ + + try doTest(alloc, &content, "hello world"); + try doTest(alloc, &p, "<p>hello world</p>"); + + var div = init(alloc, .content, "div"); + defer div.deinit(); + try div.setAttribute("class", "foo-bar"); + var in2 = [_]*Self{&p, &content}; + div.content = &in2; + + try doTest(alloc, &div, "<div class=\"foo-bar\"><p>hello world</p>hello world</div>"); +} |
