aboutsummaryrefslogtreecommitdiff
path: root/src/dom
diff options
context:
space:
mode:
Diffstat (limited to 'src/dom')
-rw-r--r--src/dom/Element.zig241
-rw-r--r--src/dom/html.zig47
2 files changed, 0 insertions, 288 deletions
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, "</");
- try acc.appendSlice(alloc, tag);
- try acc.append(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, "<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, "<img src=\"foo\" alt=\"bar\">");
-
- var img2 = try initImg(alloc, "foo", "bar");
- defer img2.deinit();
- try doTest(alloc, &img2, "<img src=\"foo\" alt=\"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, "<p>hello world</p>");
-
- 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, "<div class=\"foo-bar\"><p>hello world</p><img src=\"example.org\" alt=\"example\"></div>");
-}
diff --git a/src/dom/html.zig b/src/dom/html.zig
deleted file mode 100644
index 064ebb1..0000000
--- a/src/dom/html.zig
+++ /dev/null
@@ -1,47 +0,0 @@
-const std = @import("std");
-const eql = std.mem.eql;
-
-pub const Error = error{InvalidUtf8} || std.mem.Allocator.Error;
-
-pub fn escape(alloc: std.mem.Allocator, v: []const u8) Error![]const u8 {
- var acc = try std.ArrayList(u8).initCapacity(alloc, v.len);
- errdefer acc.deinit(alloc);
- const view = try std.unicode.Utf8View.init(v);
- var iter = view.iterator();
- while (iter.nextCodepointSlice()) |rune| {
- if (eql(u8, rune, "&")) {
- try acc.appendSlice(alloc, "&amp;");
- } else if (eql(u8, rune, "'")) {
- try acc.appendSlice(alloc, "&#39;");
- } else if (eql(u8, rune, "<")) {
- try acc.appendSlice(alloc, "&lt;");
- } else if (eql(u8, rune, ">")) {
- try acc.appendSlice(alloc, "&gt;");
- } else if (eql(u8, rune, "\"")) {
- try acc.appendSlice(alloc, "&#34;");
- } else {
- try acc.appendSlice(alloc, rune);
- }
- }
- return acc.toOwnedSlice(alloc);
-}
-
-fn doTest(alloc: std.mem.Allocator, el: []const u8, exp: []const u8) !void {
- const got = try escape(alloc, el);
- defer alloc.free(got);
- std.testing.expect(eql(u8, got, exp)) catch |err| {
- std.debug.print("{s}\n", .{got});
- return err;
- };
-}
-
-test "escaping html" {
- const alloc = std.testing.allocator;
-
- try doTest(alloc, "hello world", "hello world");
- try doTest(alloc, "hello&world", "hello&amp;world");
- try doTest(alloc, "hello'world", "hello&#39;world");
- try doTest(alloc, "hello<world", "hello&lt;world");
- try doTest(alloc, "hello>world", "hello&gt;world");
- try doTest(alloc, "hello\"world", "hello&#34;world");
-}