From ef5c0341ca15f6862294802103b02992b29609e8 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 27 Apr 2026 17:11:08 +0200 Subject: style(html): split elements in multiple files --- src/eval/html/Content.zig | 51 ++++++++++ src/eval/html/Element.zig | 230 ++-------------------------------------------- src/eval/html/Literal.zig | 25 +++++ src/eval/html/Root.zig | 54 +++++++++++ src/eval/html/Void.zig | 104 +++++++++++++++++++++ 5 files changed, 242 insertions(+), 222 deletions(-) create mode 100644 src/eval/html/Content.zig create mode 100644 src/eval/html/Literal.zig create mode 100644 src/eval/html/Root.zig create mode 100644 src/eval/html/Void.zig diff --git a/src/eval/html/Content.zig b/src/eval/html/Content.zig new file mode 100644 index 0000000..f21bcf3 --- /dev/null +++ b/src/eval/html/Content.zig @@ -0,0 +1,51 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const List = std.ArrayList; +const Element = @import("Element.zig"); +const Error = Element.Error; + +base: Element.Void, +content: List(Element), + +pub const Self = @This(); + +pub fn init(alloc: Allocator, tag: []const u8) Error!*Self { + const v = try alloc.create(Self); + v.* = .{ + .base = .{ + .alloc = alloc, + .tag = tag, + .attributes = .init(alloc), + .class_list = .init(alloc), + }, + .content = try .initCapacity(alloc, 2), + }; + return v; +} + +pub fn element(self: *Self) Element { + return .{ .vtable = .{ .render = Self.render }, .ptr = self }; +} + +pub fn append(self: *Self, content: Element) Error!void { + return self.content.append(self.base.alloc, content); +} + +fn render(context: *anyopaque, alloc: Allocator) Error![]const u8 { + const self: *Self = @ptrCast(@alignCast(context)); + var base = self.base; + const b = try base.element().render(alloc); + defer alloc.free(b); + var acc = try List(u8).initCapacity(alloc, b.len + self.content.items.len); + try acc.appendSlice(alloc, b); + for (self.content.items) |it| { + var v = it; + const sub = try v.render(alloc); + defer alloc.free(sub); + try acc.appendSlice(alloc, sub); + } + try acc.appendSlice(alloc, "'); + return acc.toOwnedSlice(alloc); +} diff --git a/src/eval/html/Element.zig b/src/eval/html/Element.zig index a0e2a02..6bccc0f 100644 --- a/src/eval/html/Element.zig +++ b/src/eval/html/Element.zig @@ -5,6 +5,11 @@ const eql = std.mem.eql; const List = std.ArrayList; const html = @import("html.zig"); +pub const Void = @import("Void.zig"); +pub const Content = @import("Content.zig"); +pub const Literal = @import("Literal.zig"); +pub const Root = @import("Root.zig"); + pub const Error = html.Error || Allocator.Error; const Element = @This(); @@ -18,225 +23,6 @@ pub fn render(self: Element, alloc: Allocator) Error![]const u8 { return self.vtable.render(self.ptr, alloc); } -fn renderAttribute(alloc: Allocator, attributes: *std.StringArrayHashMap([]const u8), class_list: *std.BufSet) Error!?[]const u8 { - const class = try renderClass(alloc, class_list); - defer if (class) |it| { - _ = attributes.orderedRemove("class"); - alloc.free(it); - }; - if (class) |it| try attributes.put("class", it); - var iter = attributes.iterator(); - if (iter.len == 0) return null; - var acc = try List(u8).initCapacity(alloc, iter.len); - 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, "=\""); - const escape = try html.escape(alloc, it.value_ptr.*); - defer alloc.free(escape); - try acc.appendSlice(alloc, escape); - try acc.append(alloc, '"'); - if (i < iter.len - 1) try acc.append(alloc, ' '); - } - return try acc.toOwnedSlice(alloc); -} - -fn renderClass(alloc: Allocator, class_list: *std.BufSet) Error!?[]const u8 { - const n = class_list.count(); - if (n == 0) return null; - var acc = try List(u8).initCapacity(alloc, n); - errdefer acc.deinit(alloc); - var iter = class_list.iterator(); - var i: usize = 0; - while (iter.next()) |it| : (i += 1) { - try acc.appendSlice(alloc, it.*); - if (i < n - 1) try acc.append(alloc, ' '); - } - return try acc.toOwnedSlice(alloc); -} - -pub const Void = struct { - alloc: Allocator, - tag: []const u8, - attributes: std.StringArrayHashMap([]const u8), - class_list: std.BufSet, - - pub const Self = @This(); - - pub fn init(alloc: Allocator, tag: []const u8) Error!*Self { - const v = try alloc.create(Self); - v.* = .{ - .alloc = alloc, - .tag = tag, - .attributes = .init(alloc), - .class_list = .init(alloc), - }; - return v; - } - - pub fn element(self: *Self) Element { - return .{ .vtable = .{ .render = Self.render }, .ptr = self }; - } - - pub fn setAttribute(self: *Self, k: []const u8, v: []const u8) Error!void { - try self.attributes.put(try self.alloc.dupe(u8, k), try self.alloc.dupe(u8, 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); - } - - pub fn appendClass(self: *Self, v: []const u8) Error!void { - try self.class_list.insert(v); - } - - pub fn hasClass(self: *Self, v: []const u8) bool { - return self.class_list.contains(v); - } - - pub fn removeClass(self: *Self, v: []const u8) void { - self.class_list.remove(v); - } - - fn render(context: *anyopaque, alloc: Allocator) Error![]const u8 { - const self: *Self = @ptrCast(@alignCast(context)); - 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); - errdefer acc.deinit(alloc); - try acc.append(alloc, '<'); - try acc.appendSlice(alloc, self.tag); - if (attr) |it| try acc.appendSlice(alloc, it); - try acc.append(alloc, '>'); - return acc.toOwnedSlice(alloc); - } -}; - -pub const Content = struct { - base: Void, - content: List(Element), - - pub const Self = @This(); - - pub fn init(alloc: Allocator, tag: []const u8) Error!*Self { - const v = try alloc.create(Self); - v.* = .{ - .base = .{ - .alloc = alloc, - .tag = tag, - .attributes = .init(alloc), - .class_list = .init(alloc), - }, - .content = try .initCapacity(alloc, 2), - }; - return v; - } - - pub fn element(self: *Self) Element { - return .{ .vtable = .{ .render = Self.render }, .ptr = self }; - } - - pub fn append(self: *Self, content: Element) Error!void { - return self.content.append(self.base.alloc, content); - } - - fn render(context: *anyopaque, alloc: Allocator) Error![]const u8 { - const self: *Self = @ptrCast(@alignCast(context)); - var base = self.base; - const b = try base.element().render(alloc); - defer alloc.free(b); - var acc = try List(u8).initCapacity(alloc, b.len + self.content.items.len); - try acc.appendSlice(alloc, b); - for (self.content.items) |it| { - var v = it; - const sub = try v.render(alloc); - defer alloc.free(sub); - try acc.appendSlice(alloc, sub); - } - try acc.appendSlice(alloc, "'); - return acc.toOwnedSlice(alloc); - } -}; - -pub const Literal = struct { - literal: []const u8, - - const Self = @This(); - - pub fn init(alloc: Allocator, literal: []const u8) Error!*Literal { - const v = try alloc.create(Self); - v.* = .{ .literal = try html.escape(alloc, literal) }; - return v; - } - - pub fn element(self: *Self) Element { - return .{ .vtable = .{ .render = Self.render }, .ptr = self }; - } - - fn render(context: *anyopaque, alloc: Allocator) Error![]const u8 { - const self: *Self = @ptrCast(@alignCast(context)); - return try alloc.dupe(u8, self.literal); - } -}; - -pub const Root = struct { - content: List(Element), - arena: Arena, - - const Self = @This(); - - pub fn init(parent: Allocator) Error!*Self { - var s = Self{ - .content = undefined, - .arena = .init(parent), - }; - var alloc = s.arena.allocator(); - s.content = try .initCapacity(alloc, 2); - const v = try alloc.create(Self); - v.* = s; - return v; - } - - pub fn deinit(self: *Self) void { - self.arena.deinit(); - } - - pub fn element(self: *Self) Element { - return .{ .vtable = .{ .render = Self.render, }, .ptr = self }; - } - - pub fn allocator(self: *Self) Allocator { - return self.arena.allocator(); - } - - pub fn append(self: *Self, el: Element) Error!void { - try self.content.append(self.allocator(), el); - } - - fn render(context: *anyopaque, alloc: Allocator) Error![]const u8 { - const self: *Self = @ptrCast(@alignCast(context)); - if (self.content.items.len == 0) return ""; - var acc = try List(u8).initCapacity(alloc, self.content.items.len); - errdefer acc.deinit(alloc); - - var arena = Arena.init(alloc); - defer arena.deinit(); - for (self.content.items) |it| { - const res = try it.render(arena.allocator()); - try acc.appendSlice(alloc, res); - } - return acc.toOwnedSlice(alloc); - } -}; - fn doTest(alloc: Allocator, el: Element, exp: []const u8) !void { const got = try el.render(alloc); defer alloc.free(got); @@ -246,7 +32,7 @@ fn doTest(alloc: Allocator, el: Element, exp: []const u8) !void { }; } -test "void element" { +test "void" { var arena = Arena.init(std.testing.allocator); defer arena.deinit(); const alloc = arena.allocator(); @@ -262,7 +48,7 @@ test "void element" { try doTest(alloc, img.element(), "\"bar\""); } -test "content element" { +test "content" { var arena = Arena.init(std.testing.allocator); defer arena.deinit(); const alloc = arena.allocator(); @@ -283,7 +69,7 @@ test "content element" { try doTest(alloc, div.element(), "

hello world


"); } -test "root element" { +test "root" { const root = try Root.init(std.testing.allocator); defer root.deinit(); const alloc = root.allocator(); diff --git a/src/eval/html/Literal.zig b/src/eval/html/Literal.zig new file mode 100644 index 0000000..d3b697f --- /dev/null +++ b/src/eval/html/Literal.zig @@ -0,0 +1,25 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const List = std.ArrayList; +const html = @import("html.zig"); +const Element = @import("Element.zig"); +const Error = Element.Error; + +literal: []const u8, + +const Self = @This(); + +pub fn init(alloc: Allocator, literal: []const u8) Error!*Element.Literal { + const v = try alloc.create(Self); + v.* = .{ .literal = try html.escape(alloc, literal) }; + return v; +} + +pub fn element(self: *Self) Element { + return .{ .vtable = .{ .render = Self.render }, .ptr = self }; +} + +fn render(context: *anyopaque, alloc: Allocator) Error![]const u8 { + const self: *Self = @ptrCast(@alignCast(context)); + return try alloc.dupe(u8, self.literal); +} diff --git a/src/eval/html/Root.zig b/src/eval/html/Root.zig new file mode 100644 index 0000000..80c556a --- /dev/null +++ b/src/eval/html/Root.zig @@ -0,0 +1,54 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const Arena = std.heap.ArenaAllocator; +const List = std.ArrayList; +const Element = @import("Element.zig"); +const Error = Element.Error; + +content: List(Element), +arena: Arena, + +const Self = @This(); + +pub fn init(parent: Allocator) Error!*Self { + var s = Self{ + .content = undefined, + .arena = .init(parent), + }; + var alloc = s.arena.allocator(); + s.content = try .initCapacity(alloc, 2); + const v = try alloc.create(Self); + v.* = s; + return v; +} + +pub fn deinit(self: *Self) void { + self.arena.deinit(); +} + +pub fn element(self: *Self) Element { + return .{ .vtable = .{ .render = Self.render, }, .ptr = self }; +} + +pub fn allocator(self: *Self) Allocator { + return self.arena.allocator(); +} + +pub fn append(self: *Self, el: Element) Error!void { + try self.content.append(self.allocator(), el); +} + +fn render(context: *anyopaque, alloc: Allocator) Error![]const u8 { + const self: *Self = @ptrCast(@alignCast(context)); + if (self.content.items.len == 0) return ""; + var acc = try List(u8).initCapacity(alloc, self.content.items.len); + errdefer acc.deinit(alloc); + + var arena = Arena.init(alloc); + defer arena.deinit(); + for (self.content.items) |it| { + const res = try it.render(arena.allocator()); + try acc.appendSlice(alloc, res); + } + return acc.toOwnedSlice(alloc); +} diff --git a/src/eval/html/Void.zig b/src/eval/html/Void.zig new file mode 100644 index 0000000..58550f4 --- /dev/null +++ b/src/eval/html/Void.zig @@ -0,0 +1,104 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const List = std.ArrayList; +const html = @import("html.zig"); +const Element = @import("Element.zig"); +const Error = Element.Error; + +alloc: Allocator, +tag: []const u8, +attributes: std.StringArrayHashMap([]const u8), +class_list: std.BufSet, + +pub const Self = @This(); + +pub fn init(alloc: Allocator, tag: []const u8) Error!*Self { + const v = try alloc.create(Self); + v.* = .{ + .alloc = alloc, + .tag = tag, + .attributes = .init(alloc), + .class_list = .init(alloc), + }; + return v; +} + +pub fn element(self: *Self) Element { + return .{ .vtable = .{ .render = Self.render }, .ptr = self }; +} + +pub fn setAttribute(self: *Self, k: []const u8, v: []const u8) Error!void { + try self.attributes.put(try self.alloc.dupe(u8, k), try self.alloc.dupe(u8, 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); +} + +pub fn appendClass(self: *Self, v: []const u8) Error!void { + try self.class_list.insert(v); +} + +pub fn hasClass(self: *Self, v: []const u8) bool { + return self.class_list.contains(v); +} + +pub fn removeClass(self: *Self, v: []const u8) void { + self.class_list.remove(v); +} + +fn render(context: *anyopaque, alloc: Allocator) Error![]const u8 { + const self: *Self = @ptrCast(@alignCast(context)); + 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); + errdefer acc.deinit(alloc); + try acc.append(alloc, '<'); + try acc.appendSlice(alloc, self.tag); + if (attr) |it| try acc.appendSlice(alloc, it); + try acc.append(alloc, '>'); + return acc.toOwnedSlice(alloc); +} + +fn renderAttribute(alloc: Allocator, attributes: *std.StringArrayHashMap([]const u8), class_list: *std.BufSet) Error!?[]const u8 { + const class = try renderClass(alloc, class_list); + defer if (class) |it| { + _ = attributes.orderedRemove("class"); + alloc.free(it); + }; + if (class) |it| try attributes.put("class", it); + var iter = attributes.iterator(); + if (iter.len == 0) return null; + var acc = try List(u8).initCapacity(alloc, iter.len); + 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, "=\""); + const escape = try html.escape(alloc, it.value_ptr.*); + defer alloc.free(escape); + try acc.appendSlice(alloc, escape); + try acc.append(alloc, '"'); + if (i < iter.len - 1) try acc.append(alloc, ' '); + } + return try acc.toOwnedSlice(alloc); +} + +fn renderClass(alloc: Allocator, class_list: *std.BufSet) Error!?[]const u8 { + const n = class_list.count(); + if (n == 0) return null; + var acc = try List(u8).initCapacity(alloc, n); + errdefer acc.deinit(alloc); + var iter = class_list.iterator(); + var i: usize = 0; + while (iter.next()) |it| : (i += 1) { + try acc.appendSlice(alloc, it.*); + if (i < n - 1) try acc.append(alloc, ' '); + } + return try acc.toOwnedSlice(alloc); +} -- cgit v1.2.3