From c008d747534f4c8aa59b045efbca754618e14b41 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Sat, 25 Apr 2026 17:18:00 +0200 Subject: style(eval): move in its own package --- src/dom/Element.zig | 241 ---------------------------------------------------- 1 file changed, 241 deletions(-) delete mode 100644 src/dom/Element.zig (limited to 'src/dom/Element.zig') diff --git a/src/dom/Element.zig b/src/dom/Element.zig deleted file mode 100644 index 87fd876..0000000 --- a/src/dom/Element.zig +++ /dev/null @@ -1,241 +0,0 @@ -const std = @import("std"); -const Allocator = std.mem.Allocator; -const eql = std.mem.eql; -const html = @import("html.zig"); - -pub const Kind = enum { - void, - content, - literal, -}; - -pub const Error = html.Error || Allocator.Error; - -const Self = @This(); - -kind: Kind, -arena: std.heap.ArenaAllocator, -tag: ?[]const u8 = null, -attributes: std.StringArrayHashMap([]const u8), -class_list: std.BufSet, -content: std.ArrayList(Self) = .empty, -literal: ?[]const u8 = null, - -/// Init a new Element with the given kind. -/// The tag will never be escaped. -/// It always duplicates strings. -pub fn init(alloc: Allocator, knd: Kind, tag: []const u8) Error!Self { - var v = Self{ - .kind = knd, - .arena = .init(alloc), - .attributes = .init(alloc), - .class_list = .init(alloc), - }; - var a = v.arena.allocator(); - v.tag = try a.dupe(u8, tag); - return v; -} - -pub fn initEmpty(alloc: Allocator) Self { - return .{ - .kind = .content, - .arena = .init(alloc), - .attributes = .init(alloc), - .class_list = .init(alloc), - }; -} - -/// Init a new literal element. -/// The literal content will never be escaped, see initLitEscaped if you want to escape it. -/// It always duplicates strings. -pub fn initLit(alloc: Allocator, literal: []const u8) Error!Self { - var v = Self{ - .kind = .literal, - .arena = .init(alloc), - .attributes = .init(alloc), - .class_list = .init(alloc), - }; - var a = v.arena.allocator(); - v.literal = try a.dupe(u8, literal); - return v; -} - -/// Init a new literal element that is escaped. -/// The literal content will be escaped, see initLit if you don't want this behavior. -/// It always duplicates strings. -pub fn initLitEscaped(alloc: Allocator, literal: []const u8) Error!Self { - const escaped = try html.escape(alloc, literal); - defer alloc.free(escaped); - return .initLit(alloc, escaped); -} - -pub fn deinit(self: *Self) void { - self.attributes.deinit(); - self.class_list.deinit(); - for (self.content.items) |it| { - var v = it; - v.deinit(); - } - self.content.deinit(self.arena.allocator()); - self.arena.deinit(); -} - -pub fn render(self: *Self, alloc: Allocator) Error![]const u8 { - const attr = try self.renderAttribute(alloc); - defer if (attr) |it| alloc.free(it); - var acc = try std.ArrayList(u8).initCapacity(alloc, self.content.items.len + if (self.literal) |it| it.len else 0); - errdefer acc.deinit(alloc); - if (self.tag) |tag| { - try acc.append(alloc, '<'); - try acc.appendSlice(alloc, tag); - if (attr) |it| try acc.appendSlice(alloc, it); - try acc.append(alloc, '>'); - } - switch (self.kind) { - .void => return acc.toOwnedSlice(alloc), - .content => { - for (self.content.items) |it| { - var v = it; - const sub = try v.render(alloc); - defer alloc.free(sub); - try acc.appendSlice(alloc, sub); - } - }, - .literal => try acc.appendSlice(alloc, self.literal.?), - } - if (self.tag) |tag| { - try acc.appendSlice(alloc, "'); - } - return acc.toOwnedSlice(alloc); -} - -fn renderAttribute(self: *Self, alloc: Allocator) Error!?[]const u8 { - const class = try self.renderClass(alloc); - defer if (class) |it| alloc.free(it); - if (class) |it| try self.setAttribute("class", it); - var iter = self.attributes.iterator(); - if (iter.len == 0) return null; - var acc = try std.ArrayList(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(self: *const Self, alloc: Allocator) Error!?[]const u8 { - var iter = self.class_list.iterator(); - if (iter.len == 0) return null; - const n = self.class_list.count(); - var acc = try std.ArrayList(u8).initCapacity(alloc, n); - errdefer acc.deinit(alloc); - 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 fn setAttribute(self: *Self, k: []const u8, v: []const u8) Error!void { - var alloc = self.arena.allocator(); - try self.attributes.put(try alloc.dupe(u8, k), try 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 { - var alloc = self.arena.allocator(); - try self.class_list.insert(try alloc.dupe(u8, 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); -} - -pub fn appendContent(self: *Self, content: Self) Error!void { - const alloc = self.arena.allocator(); - return self.content.append(alloc, content); -} - -pub fn initImg(alloc: Allocator, src: []const u8, alt: []const u8) Error!Self { - var el = try init(alloc, .void, "img"); - try el.setAttribute("src", src); - try el.setAttribute("alt", alt); - return el; -} - -pub fn initContent(alloc: Allocator, tag: []const u8, content: []Self) Error!Self { - var el = try init(alloc, .content, tag); - for (content) |it| try el.appendContent(it); - return el; -} - -fn doTest(alloc: Allocator, el: *Self, exp: []const u8) !void { - const got = try el.render(alloc); - defer alloc.free(got); - std.testing.expect(eql(u8, got, exp)) catch |err| { - std.debug.print("{s}\n", .{got}); - return err; - }; -} - -test "void element" { - const alloc = std.testing.allocator; - - var br = try init(alloc, .void, "br"); - defer br.deinit(); - - try doTest(alloc, &br, "
"); - - var img = try init(alloc, .void, "img"); - defer img.deinit(); - try img.setAttribute("src", "foo"); - try img.setAttribute("alt", "bar"); - - try doTest(alloc, &img, "\"bar\""); - - var img2 = try initImg(alloc, "foo", "bar"); - defer img2.deinit(); - try doTest(alloc, &img2, "\"bar\""); -} - -test "content element" { - const alloc = std.testing.allocator; - - var p = try init(alloc, .content, "p"); - - var content = try initLit(alloc, "hello world"); - try p.appendContent(content); - - try doTest(alloc, &content, "hello world"); - try doTest(alloc, &p, "

hello world

"); - - var div = try init(alloc, .content, "div"); - defer div.deinit(); - try div.appendClass("foo-bar"); - try div.appendContent(p); - try div.appendContent(try initImg(alloc, "example.org", "example")); - - try doTest(alloc, &div, "

hello world

\"example\"
"); -} -- cgit v1.2.3