diff options
| author | Anhgelus Morhtuuzh <william@herges.fr> | 2026-04-30 17:56:55 +0200 |
|---|---|---|
| committer | Anhgelus Morhtuuzh <william@herges.fr> | 2026-04-30 17:56:55 +0200 |
| commit | a13df3ca580dc31544ef092d5d37e18089af3517 (patch) | |
| tree | cfcf8c1d8322ee8e97783e263105eac9f5c00f33 | |
| parent | 5ecd48d4b9f928beb4143f88e802ee4d9e25a2bd (diff) | |
refactor(element): generalize element creation
| -rw-r--r-- | src/eval/Element.zig | 52 | ||||
| -rw-r--r-- | src/eval/Image.zig | 10 | ||||
| -rw-r--r-- | src/eval/Root.zig | 10 | ||||
| -rw-r--r-- | src/eval/Title.zig | 24 | ||||
| -rw-r--r-- | src/eval/blocks.zig | 43 | ||||
| -rw-r--r-- | src/eval/html/Content.zig | 4 | ||||
| -rw-r--r-- | src/eval/html/Element.zig | 9 | ||||
| -rw-r--r-- | src/eval/html/Literal.zig | 4 | ||||
| -rw-r--r-- | src/eval/html/Root.zig | 4 | ||||
| -rw-r--r-- | src/eval/html/Void.zig | 4 | ||||
| -rw-r--r-- | src/eval/list.zig | 10 | ||||
| -rw-r--r-- | src/eval/math.zig | 11 | ||||
| -rw-r--r-- | src/eval/paragraph.zig | 10 |
13 files changed, 77 insertions, 118 deletions
diff --git a/src/eval/Element.zig b/src/eval/Element.zig index 228e36c..9e8f70e 100644 --- a/src/eval/Element.zig +++ b/src/eval/Element.zig @@ -13,6 +13,38 @@ pub const Callout = blocks.Callout; pub const Quote = blocks.Quote; pub const Math = @import("math.zig"); +pub fn Wrapper(comptime V: type, comptime h: *const fn (*V, Allocator) HTML.Error!HTML) type { + comptime { + if (!@hasField(V, "node")) @compileError("missing field 'node' for " ++ @typeName(V)); + const nd = @FieldType(V, "node"); + if (nd != Node) @compileError("invalid node's type: " ++ @typeName(nd) ++ ", want " ++ @typeName(Node)); + if (!std.meta.hasMethod(V, "element")) @compileError("missing declaration 'element' for " ++ @typeName(V)); + } + return struct { + ptr: *V, + + const Self = @This(); + + fn node(context: *anyopaque) *Node { + const self: *V = @ptrCast(@alignCast(context)); + return &self.node; + } + + fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { + const self: *V = @ptrCast(@alignCast(context)); + return try h(self, alloc); + } + + pub fn init(ptr: *V) Element { + return (Self{ .ptr = ptr }).element(); + } + + pub fn element(self: Self) Element { + return .{ .ptr = self.ptr, .vtable = .{ .node = Self.node, .html = Self.html } }; + } + }; +} + pub const Node = struct { ptr: *anyopaque, vtable: struct { element: *const fn (*anyopaque) Element }, @@ -68,12 +100,7 @@ pub const Literal = struct { } pub fn element(self: *Self) Element { - return .{ .ptr = self, .vtable = .{ .html = Self.html, .node = getNode } }; - } - - fn getNode(context: *anyopaque) *Node { - const self: *Self = @ptrCast(@alignCast(context)); - return &self.node; + return Wrapper(Self, Self.html).init(self); } fn fromNode(context: *anyopaque) Element { @@ -81,8 +108,7 @@ pub const Literal = struct { return self.element(); } - fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { - const self: *Self = @ptrCast(@alignCast(context)); + fn html(self: *Self, alloc: Allocator) HTML.Error!HTML { return (try HTML.Literal.init(alloc, self.content)).element(); } }; @@ -101,7 +127,7 @@ pub fn Simple(comptime tag: []const u8) type { } pub fn element(self: *Self) Element { - return .{ .ptr = self, .vtable = .{ .html = Self.html, .node = getNode } }; + return Wrapper(Self, Self.html).init(self); } pub fn toTag(self: *Self, alloc: Allocator, comptime target: []const u8) !*Simple(target) { @@ -118,18 +144,12 @@ pub fn Simple(comptime tag: []const u8) type { return el; } - fn getNode(context: *anyopaque) *Node { - const self: *Self = @ptrCast(@alignCast(context)); - return &self.node; - } - fn fromNode(context: *anyopaque) Element { const self: *Self = @ptrCast(@alignCast(context)); return self.element(); } - fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { - const self: *Self = @ptrCast(@alignCast(context)); + fn html(self: *Self, alloc: Allocator) HTML.Error!HTML { var el = try HTML.Content.init(alloc, tag); if (self.content) |it| el.content = try it.html(alloc); return el.element(); diff --git a/src/eval/Image.zig b/src/eval/Image.zig index 3e0132a..ef4f398 100644 --- a/src/eval/Image.zig +++ b/src/eval/Image.zig @@ -21,12 +21,7 @@ pub fn init(alloc: Allocator, src: []const u8) !*Self { } pub fn element(self: *Self) Element { - return .{ .ptr = self, .vtable = .{ .html = html, .node = getNode } }; -} - -fn getNode(context: *anyopaque) *Node { - const self: *Self = @ptrCast(@alignCast(context)); - return &self.node; + return Element.Wrapper(Self, html).init(self); } fn fromNode(context: *anyopaque) Element { @@ -34,8 +29,7 @@ fn fromNode(context: *anyopaque) Element { return self.element(); } -fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { - const self: *Self = @ptrCast(@alignCast(context)); +fn html(self: *Self, alloc: Allocator) HTML.Error!HTML { var img = try HTML.Void.init(alloc, "img"); try img.setAttribute("src", self.src); if (self.alt) |it| try img.setAttribute("alt", it); diff --git a/src/eval/Root.zig b/src/eval/Root.zig index 048dd3b..15c03e8 100644 --- a/src/eval/Root.zig +++ b/src/eval/Root.zig @@ -42,25 +42,19 @@ pub fn append(self: *Self, raw: anytype) void { } pub fn element(self: *Self) Element { - return .{ .vtable = .{ .html = html, .node = getNode }, .ptr = self }; + return Element.Wrapper(Self, html).init(self); } pub fn renderHTML(self: *Self, alloc: Allocator) HTML.Error![]const u8 { return try self.element().renderHTML(alloc); } -fn getNode(context: *anyopaque) *Node { - const self: *Self = @ptrCast(@alignCast(context)); - return &self.node; -} - fn fromNode(context: *anyopaque) Element { const self: *Self = @ptrCast(@alignCast(context)); return self.element(); } -fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { - const self: *Self = @ptrCast(@alignCast(context)); +fn html(self: *Self, alloc: Allocator) HTML.Error!HTML { const el = try HTML.Root.init(alloc); var v = self.content.first; while (v) |it| : (v = it.next) try el.append(Node.from(it).element()); diff --git a/src/eval/Title.zig b/src/eval/Title.zig index 1841ce7..1300832 100644 --- a/src/eval/Title.zig +++ b/src/eval/Title.zig @@ -1,11 +1,11 @@ const std = @import("std"); const Allocator = std.mem.Allocator; -const HTML = Parent.HTML; -const Parent = @import("Element.zig"); -const Node = Parent.Node; +const HTML = Element.HTML; +const Element = @import("Element.zig"); +const Node = Element.Node; level: u3, -content: Parent, +content: Element, node: Node = .{ .ptr = undefined, .vtable = .{ .element = fromNode }, @@ -13,29 +13,23 @@ node: Node = .{ const Self = @This(); -pub fn init(alloc: Allocator, level: u3, content: Parent) !*Self { +pub fn init(alloc: Allocator, level: u3, content: Element) !*Self { const v = try alloc.create(Self); v.* = .{ .level = level, .content = content }; v.node.ptr = v; return v; } -pub fn element(self: *Self) Parent { - return .{ .ptr = self, .vtable = .{ .html = html, .node = getNode } }; +pub fn element(self: *Self) Element { + return Element.Wrapper(Self, html).init(self); } -fn getNode(context: *anyopaque) *Node { - const self: *Self = @ptrCast(@alignCast(context)); - return &self.node; -} - -fn fromNode(context: *anyopaque) Parent { +fn fromNode(context: *anyopaque) Element { const self: *Self = @ptrCast(@alignCast(context)); return self.element(); } -fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { - const self: *Self = @ptrCast(@alignCast(context)); +fn html(self: *Self, alloc: Allocator) HTML.Error!HTML { var el = try HTML.Content.init(alloc, switch (self.level) { 1 => "h1", 2 => "h2", diff --git a/src/eval/blocks.zig b/src/eval/blocks.zig index 9357799..03b0d00 100644 --- a/src/eval/blocks.zig +++ b/src/eval/blocks.zig @@ -22,15 +22,7 @@ pub const Code = struct { } pub fn element(self: *Self) Element { - return .{ .ptr = self, .vtable = .{ - .html = html, - .node = getNode, - } }; - } - - fn getNode(context: *anyopaque) *Node { - const self: *Self = @ptrCast(@alignCast(context)); - return &self.node; + return Element.Wrapper(Self, html).init(self); } fn fromNode(context: *anyopaque) Element { @@ -38,8 +30,7 @@ pub const Code = struct { return self.element(); } - fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { - const self: *Self = @ptrCast(@alignCast(context)); + fn html(self: *Self, alloc: Allocator) HTML.Error!HTML { var el = try HTML.Content.init(alloc, "pre"); if (self.attribute) |attr| try el.base.setAttribute("data-code", attr); var code = try HTML.Content.init(alloc, "code"); @@ -69,12 +60,7 @@ pub const Figure = struct { } pub fn element(self: *Self) Element { - return .{ .ptr = self, .vtable = .{ .html = html, .node = getNode } }; - } - - fn getNode(context: *anyopaque) *Node { - const self: *Self = @ptrCast(@alignCast(context)); - return &self.node; + return Element.Wrapper(Self, html).init(self); } fn fromNode(context: *anyopaque) Element { @@ -82,8 +68,7 @@ pub const Figure = struct { return self.element(); } - fn html(context: *anyopaque, parent: Allocator) HTML.Error!HTML { - const self: *Self = @ptrCast(@alignCast(context)); + fn html(self: *Self, parent: Allocator) HTML.Error!HTML { var el = try HTML.Content.init(parent, "figure"); var root = try HTML.Root.init(parent); const alloc = root.allocator(); @@ -116,12 +101,7 @@ pub const Callout = struct { } pub fn element(self: *Self) Element { - return .{ .ptr = self, .vtable = .{ .html = html, .node = getNode } }; - } - - fn getNode(context: *anyopaque) *Node { - const self: *Self = @ptrCast(@alignCast(context)); - return &self.node; + return Element.Wrapper(Self, html).init(self); } fn fromNode(context: *anyopaque) Element { @@ -129,8 +109,7 @@ pub const Callout = struct { return self.element(); } - fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { - const self: *Self = @ptrCast(@alignCast(context)); + fn html(self: *Self, alloc: Allocator) HTML.Error!HTML { var el = try HTML.Content.init(alloc, "div"); try el.base.appendClass("callout"); const kind = self.kind orelse "default"; @@ -162,12 +141,7 @@ pub const Quote = struct { } pub fn element(self: *Self) Element { - return .{ .ptr = self, .vtable = .{ .html = html, .node = getNode } }; - } - - fn getNode(context: *anyopaque) *Node { - const self: *Self = @ptrCast(@alignCast(context)); - return &self.node; + return Element.Wrapper(Self, html).init(self); } fn fromNode(context: *anyopaque) Element { @@ -175,8 +149,7 @@ pub const Quote = struct { return self.element(); } - fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { - const self: *Self = @ptrCast(@alignCast(context)); + fn html(self: *Self, alloc: Allocator) HTML.Error!HTML { const quote = try HTML.Content.init(alloc, "blockquote"); quote.content = try self.content.html(alloc); return quote.element(); diff --git a/src/eval/html/Content.zig b/src/eval/html/Content.zig index 96961da..7b9da3e 100644 --- a/src/eval/html/Content.zig +++ b/src/eval/html/Content.zig @@ -28,7 +28,7 @@ pub fn init(alloc: Allocator, tag: []const u8) Error!*Self { } pub fn element(self: *Self) Element { - return (Element.Wrapper(Self){ .ptr = self }).element(); + return Element.Wrapper(Self, render).init(self); } fn fromNode(context: *anyopaque) Element { @@ -36,7 +36,7 @@ fn fromNode(context: *anyopaque) Element { return self.element(); } -pub fn render(self: *Self, alloc: Allocator) Error![]const u8 { +fn render(self: *Self, alloc: Allocator) Error![]const u8 { var base = self.base; const b = try base.element().render(alloc); defer alloc.free(b); diff --git a/src/eval/html/Element.zig b/src/eval/html/Element.zig index 52ee58d..dd24bea 100644 --- a/src/eval/html/Element.zig +++ b/src/eval/html/Element.zig @@ -11,13 +11,12 @@ pub const Root = @import("Root.zig"); pub const Error = html.Error || Allocator.Error; -pub fn Wrapper(comptime V: type) type { +pub fn Wrapper(comptime V: type, comptime r: *const fn (*V, Allocator) Error![]const u8) type { comptime { if (!@hasField(V, "node")) @compileError("missing field 'node' for " ++ @typeName(V)); const nd = @FieldType(V, "node"); if (nd != Node) @compileError("invalid node's type: " ++ @typeName(nd) ++ ", want " ++ @typeName(Node)); if (!std.meta.hasMethod(V, "element")) @compileError("missing declaration 'element' for " ++ @typeName(V)); - if (!std.meta.hasMethod(V, "render")) @compileError("missing declaration 'render' for " ++ @typeName(V)); } return struct { ptr: *V, @@ -31,7 +30,11 @@ pub fn Wrapper(comptime V: type) type { fn render(context: *anyopaque, alloc: Allocator) Error![]const u8 { const self: *V = @ptrCast(@alignCast(context)); - return try self.render(alloc); + return try r(self, alloc); + } + + pub fn init(ptr: *V) Element { + return (Self{ .ptr = ptr }).element(); } pub fn element(self: Self) Element { diff --git a/src/eval/html/Literal.zig b/src/eval/html/Literal.zig index 9d6d91e..a5eb199 100644 --- a/src/eval/html/Literal.zig +++ b/src/eval/html/Literal.zig @@ -28,7 +28,7 @@ pub fn initNoEscape(alloc: Allocator, literal: []const u8) Error!*Element.Litera } pub fn element(self: *Self) Element { - return (Element.Wrapper(Self){ .ptr = self }).element(); + return Element.Wrapper(Self, render).init(self); } fn fromNode(context: *anyopaque) Element { @@ -36,6 +36,6 @@ fn fromNode(context: *anyopaque) Element { return self.element(); } -pub fn render(self: *Self, alloc: Allocator) Error![]const u8 { +fn render(self: *Self, alloc: Allocator) Error![]const u8 { return try alloc.dupe(u8, self.literal); } diff --git a/src/eval/html/Root.zig b/src/eval/html/Root.zig index 6c4c37a..af130d1 100644 --- a/src/eval/html/Root.zig +++ b/src/eval/html/Root.zig @@ -28,7 +28,7 @@ pub fn deinit(self: *Self) void { } pub fn element(self: *Self) Element { - return (Element.Wrapper(Self){ .ptr = self }).element(); + return Element.Wrapper(Self, render).init(self); } pub fn allocator(self: *Self) Allocator { @@ -50,7 +50,7 @@ fn fromNode(context: *anyopaque) Element { return self.element(); } -pub fn render(self: *Self, alloc: Allocator) Error![]const u8 { +fn render(self: *Self, alloc: Allocator) Error![]const u8 { if (self.content.first == null) return ""; var acc = try std.ArrayList(u8).initCapacity(alloc, 8); errdefer acc.deinit(alloc); diff --git a/src/eval/html/Void.zig b/src/eval/html/Void.zig index 431536c..17b369d 100644 --- a/src/eval/html/Void.zig +++ b/src/eval/html/Void.zig @@ -30,7 +30,7 @@ pub fn init(alloc: Allocator, tag: []const u8) Error!*Self { } pub fn element(self: *Self) Element { - return (Element.Wrapper(Self){ .ptr = self }).element(); + return Element.Wrapper(Self, render).init(self); } pub fn setAttribute(self: *Self, k: []const u8, v: []const u8) Error!void { @@ -62,7 +62,7 @@ fn fromNode(context: *anyopaque) Element { return self.element(); } -pub fn render(self: *Self, alloc: Allocator) Error![]const u8 { +fn render(self: *Self, alloc: Allocator) Error![]const u8 { const attr = try renderAttribute(alloc, &self.attributes, &self.class_list); defer if (attr) |it| alloc.free(it); var acc = try List(u8).initCapacity(alloc, self.tag.len + 2); diff --git a/src/eval/list.zig b/src/eval/list.zig index 160bde2..0b1306a 100644 --- a/src/eval/list.zig +++ b/src/eval/list.zig @@ -24,12 +24,7 @@ fn List(comptime tag: []const u8) type { } pub fn element(self: *Self) Element { - return .{ .ptr = self, .vtable = .{ .html = html, .node = getNode } }; - } - - fn getNode(context: *anyopaque) *Node { - const self: *Self = @ptrCast(@alignCast(context)); - return &self.node; + return Element.Wrapper(Self, html).init(self); } fn fromNode(context: *anyopaque) Element { @@ -37,8 +32,7 @@ fn List(comptime tag: []const u8) type { return self.element(); } - fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { - const self: *Self = @ptrCast(@alignCast(context)); + fn html(self: *Self, alloc: Allocator) HTML.Error!HTML { var el = try HTML.Content.init(alloc, tag); var root = try HTML.Root.init(alloc); el.content = root.element(); diff --git a/src/eval/math.zig b/src/eval/math.zig index 20d3fec..aeec997 100644 --- a/src/eval/math.zig +++ b/src/eval/math.zig @@ -54,12 +54,7 @@ fn Math(comptime template: []const u8) type { } pub fn element(self: *Self) Element { - return .{ .ptr = self, .vtable = .{ .html = html, .node = getNode } }; - } - - fn getNode(context: *anyopaque) *Node { - const self: *Self = @ptrCast(@alignCast(context)); - return &self.node; + return Element.Wrapper(Self, html).init(self); } fn fromNode(context: *anyopaque) Element { @@ -67,10 +62,8 @@ fn Math(comptime template: []const u8) type { return self.element(); } - fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { - const self: *Self = @ptrCast(@alignCast(context)); + fn html(self: *Self, alloc: Allocator) HTML.Error!HTML { const content = self.content orelse return (try HTML.Literal.init(alloc, "")).element(); - var arena = std.heap.ArenaAllocator.init(alloc); defer arena.deinit(); const escaped = try escape(arena.allocator(), content); diff --git a/src/eval/paragraph.zig b/src/eval/paragraph.zig index ca72142..3ebf29b 100644 --- a/src/eval/paragraph.zig +++ b/src/eval/paragraph.zig @@ -32,12 +32,7 @@ pub const Link = struct { } pub fn element(self: *Self) Element { - return .{ .ptr = self, .vtable = .{ .html = html, .node = getNode } }; - } - - fn getNode(context: *anyopaque) *Node { - const self: *Self = @ptrCast(@alignCast(context)); - return &self.node; + return Element.Wrapper(Self, html).init(self); } fn fromNode(context: *anyopaque) Element { @@ -45,8 +40,7 @@ pub const Link = struct { return self.element(); } - fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { - const self: *Self = @ptrCast(@alignCast(context)); + fn html(self: *Self, alloc: Allocator) HTML.Error!HTML { var el = try HTML.Content.init(alloc, "a"); el.content = try self.content.html(alloc); try el.base.setAttribute("href", self.link); |
