aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/eval/html/Content.zig51
-rw-r--r--src/eval/html/Element.zig230
-rw-r--r--src/eval/html/Literal.zig25
-rw-r--r--src/eval/html/Root.zig54
-rw-r--r--src/eval/html/Void.zig104
5 files changed, 242 insertions, 222 deletions
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, "</");
+ try acc.appendSlice(alloc, base.tag);
+ try acc.append(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, "</");
- try acc.appendSlice(alloc, base.tag);
- try acc.append(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(), "<img src=\"foo\" alt=\"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(), "<div class=\"foo-bar\"><p>hello world</p><br></div>");
}
-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);
+}