diff options
Diffstat (limited to 'src/eval/html/Void.zig')
| -rw-r--r-- | src/eval/html/Void.zig | 104 |
1 files changed, 104 insertions, 0 deletions
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); +} |
