aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Element.zig243
-rw-r--r--src/content.zig28
-rw-r--r--src/dom/Element.zig39
-rw-r--r--src/dom/html.zig4
-rw-r--r--src/link.zig31
-rw-r--r--src/paragraph.zig37
-rw-r--r--src/parser.zig5
-rw-r--r--src/testing.zig6
-rw-r--r--src/title.zig19
9 files changed, 319 insertions, 93 deletions
diff --git a/src/Element.zig b/src/Element.zig
new file mode 100644
index 0000000..7206125
--- /dev/null
+++ b/src/Element.zig
@@ -0,0 +1,243 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const DOMElement = @import("dom/Element.zig");
+
+const Parent = @This();
+
+vtable: struct {
+ deinit: *const fn (*anyopaque, Allocator) void,
+ dom: *const fn (*anyopaque, Allocator) DOMElement.Error!DOMElement,
+},
+ptr: *anyopaque,
+
+pub fn renderHTML(self: Parent, alloc: Allocator) DOMElement.Error![]const u8 {
+ var el = try self.vtable.dom(self.ptr, alloc);
+ defer el.deinit();
+ return el.render(alloc);
+}
+
+pub fn deinit(self: Parent, alloc: Allocator) void {
+ self.vtable.deinit(self.ptr, alloc);
+}
+
+fn dom(self: Parent, alloc: Allocator) DOMElement.Error!DOMElement {
+ return self.vtable.dom(self.ptr, alloc);
+}
+
+pub const Paragraph = Modifier("p");
+pub const Bold = Modifier("b");
+pub const Italic = Modifier("em");
+pub const Code = Modifier("code");
+
+pub const Empty = struct {
+ content: std.ArrayList(Parent),
+
+ const Self = @This();
+
+ pub fn init(alloc: Allocator) !*Self {
+ const v = try alloc.create(Self);
+ v.* = .{ .content = try .initCapacity(alloc, 2) };
+ return v;
+ }
+
+ pub fn element(self: *Self) Parent {
+ return .{ .ptr = self, .vtable = .{ .deinit = destroy, .dom = Self.dom } };
+ }
+
+ pub fn deinit(self: *Self, alloc: Allocator) void {
+ destroy(self, alloc);
+ }
+
+ fn destroy(context: *anyopaque, alloc: Allocator) void {
+ const self: *Self = @ptrCast(@alignCast(context));
+ for (self.content.items) |it| it.deinit(alloc);
+ self.content.deinit(alloc);
+ alloc.destroy(self);
+ }
+
+ fn dom(context: *anyopaque, alloc: Allocator) DOMElement.Error!DOMElement {
+ const self: *Self = @ptrCast(@alignCast(context));
+ var el = DOMElement.initEmpty(alloc);
+ errdefer el.deinit();
+ for (self.content.items) |it| try el.appendContent(try it.dom(alloc));
+ return el;
+ }
+};
+
+pub const Literal = struct {
+ content: []const u8,
+
+ const Self = @This();
+
+ pub fn init(alloc: Allocator, content: []const u8) !*Self {
+ const v = try alloc.create(Self);
+ v.* = .{ .content = content };
+ return v;
+ }
+
+ pub fn element(self: *Self) Parent {
+ return .{ .ptr = self, .vtable = .{ .deinit = destroy, .dom = Self.dom } };
+ }
+
+ pub fn deinit(self: *Self, alloc: Allocator) void {
+ destroy(self, alloc);
+ }
+
+ fn destroy(context: *anyopaque, alloc: Allocator) void {
+ const self: *Self = @ptrCast(@alignCast(context));
+ alloc.destroy(self);
+ }
+
+ fn dom(context: *anyopaque, alloc: Allocator) DOMElement.Error!DOMElement {
+ const self: *Self = @ptrCast(@alignCast(context));
+ return DOMElement.initLitEscaped(alloc, self.content);
+ }
+};
+
+pub fn Modifier(comptime tag: []const u8) type {
+ return struct {
+ content: std.ArrayList(Parent),
+
+ const Self = @This();
+
+ pub fn init(alloc: Allocator) !*Self {
+ const v = try alloc.create(Self);
+ v.* = .{ .content = try .initCapacity(alloc, 2) };
+ return v;
+ }
+
+ pub fn element(self: *Self) Parent {
+ return .{ .ptr = self, .vtable = .{ .deinit = destroy, .dom = Self.dom } };
+ }
+
+ pub fn deinit(self: *Self, alloc: Allocator) void {
+ destroy(self, alloc);
+ }
+
+ fn destroy(context: *anyopaque, alloc: Allocator) void {
+ var self: *Self = @ptrCast(@alignCast(context));
+ for (self.content.items) |it| it.deinit(alloc);
+ self.content.deinit(alloc);
+ alloc.destroy(self);
+ }
+
+ fn dom(context: *anyopaque, alloc: Allocator) DOMElement.Error!DOMElement {
+ const self: *Self = @ptrCast(@alignCast(context));
+ var el = try DOMElement.init(alloc, .content, tag);
+ errdefer el.deinit();
+ for (self.content.items) |it| try el.appendContent(try it.dom(alloc));
+ return el;
+ }
+ };
+}
+
+pub const Link = struct {
+ link: []const u8,
+ content: Parent,
+ target: ?[]const u8 = null,
+
+ const Self = @This();
+
+ pub fn init(alloc: Allocator, content: Parent, link: []const u8) !*Self {
+ const v = try alloc.create(Self);
+ v.* = .{
+ .content = content,
+ .link = link,
+ };
+ return v;
+ }
+
+ pub fn element(self: *Self) Parent {
+ return .{ .ptr = self, .vtable = .{ .deinit = destroy, .dom = Self.dom } };
+ }
+
+ pub fn deinit(self: *Self, alloc: Allocator) void {
+ destroy(self, alloc);
+ }
+
+ fn destroy(context: *anyopaque, alloc: Allocator) void {
+ var self: *Self = @ptrCast(@alignCast(context));
+ self.content.deinit(alloc);
+ alloc.destroy(self);
+ }
+
+ fn dom(context: *anyopaque, alloc: Allocator) DOMElement.Error!DOMElement {
+ const self: *Self = @ptrCast(@alignCast(context));
+ var el = try DOMElement.init(alloc, .content, "a");
+ errdefer el.deinit();
+ try el.appendContent(try self.content.dom(alloc));
+ try el.setAttribute("href", self.link);
+ if (self.target) |target| try el.setAttribute("target", target);
+ return el;
+ }
+};
+
+pub const Title = struct {
+ level: u3,
+ content: Parent,
+
+ const Self = @This();
+
+ pub fn init(alloc: Allocator, level: u3, content: Parent) !*Self {
+ const v = try alloc.create(Self);
+ v.* = .{ .level = level, .content = content };
+ return v;
+ }
+
+ pub fn element(self: *Self) Parent {
+ return .{ .ptr = self, .vtable = .{ .deinit = destroy, .dom = Self.dom } };
+ }
+
+ pub fn deinit(self: *Self, alloc: Allocator) void {
+ self.element().deinit(alloc);
+ }
+
+ fn destroy(context: *anyopaque, alloc: Allocator) void {
+ var self: *Self = @ptrCast(@alignCast(context));
+ self.content.deinit(alloc);
+ alloc.destroy(self);
+ }
+
+ fn dom(context: *anyopaque, alloc: Allocator) DOMElement.Error!DOMElement {
+ const self: *Self = @ptrCast(@alignCast(context));
+ var el = try DOMElement.init(alloc, .content, switch (self.level) {
+ 1 => "h1",
+ 2 => "h2",
+ 3 => "h3",
+ 4 => "h4",
+ 5 => "h5",
+ 6 => "h6",
+ else => unreachable,
+ });
+ errdefer el.deinit();
+ try el.appendContent(try self.content.dom(alloc));
+ return el;
+ }
+};
+
+fn doTest(alloc: Allocator, el: Parent, exp: []const u8) !void {
+ const got = try el.renderHTML(alloc);
+ defer alloc.free(got);
+ std.testing.expect(std.mem.eql(u8, got, exp)) catch |err| {
+ std.debug.print("{s}\n", .{got});
+ return err;
+ };
+}
+
+test "paragraph" {
+ const alloc = std.testing.allocator;
+
+ const lit = (try Literal.init(alloc, "hello world")).element();
+ try doTest(alloc, lit, "hello world");
+
+ var p = try Paragraph.init(alloc);
+ try p.content.append(alloc, lit);
+ defer p.deinit(alloc);
+ try doTest(alloc, p.element(), "<p>hello world</p>");
+
+ const link = (try Link.init(alloc, (try Literal.init(alloc, "foo")).element(), "example.org")).element();
+ try doTest(alloc, link, "<a href=\"example.org\">foo</a>");
+
+ try p.content.append(alloc, link);
+ try doTest(alloc, p.element(), "<p>hello world<a href=\"example.org\">foo</a></p>");
+}
diff --git a/src/content.zig b/src/content.zig
index 72acbc7..68d88ad 100644
--- a/src/content.zig
+++ b/src/content.zig
@@ -2,7 +2,7 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const Token = @import("lexer/Token.zig");
const Lexer = @import("lexer/Lexer.zig");
-const Element = @import("dom/Element.zig");
+const Element = @import("Element.zig");
const parser = @import("parser.zig");
const link = @import("link.zig");
const testing = @import("testing.zig");
@@ -12,33 +12,33 @@ const doTestError = testing.doError;
pub const Error = error{ ModifierNotClosed, IllegalPlacement } || Lexer.Error || Allocator.Error;
pub fn parse(alloc: Allocator, l: *Lexer) Error!Element {
- var content = Element.initEmpty(alloc);
- errdefer content.deinit();
+ var content = try Element.Empty.init(alloc);
+ errdefer content.deinit(alloc);
const v = l.next().?;
switch (v.kind) {
.literal => {
- const el = try Element.initLitEscaped(alloc, v.content);
- try content.appendContent(el);
+ const el = try Element.Literal.init(alloc, v.content);
+ try content.content.append(alloc, el.element());
},
- .bold => try content.appendContent(try parseModifier(alloc, l, .bold, "b")),
- .italic => try content.appendContent(try parseModifier(alloc, l, .italic, "em")),
- .code => try content.appendContent(try parseModifier(alloc, l, .code, "code")),
+ .bold => try content.content.append(alloc, try parseModifier(alloc, l, .bold, "b")),
+ .italic => try content.content.append(alloc, try parseModifier(alloc, l, .italic, "em")),
+ .code => try content.content.append(alloc, try parseModifier(alloc, l, .code, "code")),
else => return Error.IllegalPlacement,
}
- return content;
+ return content.element();
}
-fn parseModifier(alloc: Allocator, l: *Lexer, knd: Token.Kind, tag: []const u8) Error!Element {
- var el = try Element.init(alloc, .content, tag);
- errdefer el.deinit();
+fn parseModifier(alloc: Allocator, l: *Lexer, knd: Token.Kind, comptime tag: []const u8) Error!Element {
+ var el = try Element.Modifier(tag).init(alloc);
+ errdefer el.deinit(alloc);
while (l.peek()) |next| {
if (next.kind == knd) {
// consuming the finisher
l.consume();
- return el;
+ return el.element();
}
if (next.kind.isDelimiter()) return Error.ModifierNotClosed;
- try el.appendContent(try parse(alloc, l));
+ try el.content.append(alloc, try parse(alloc, l));
}
return Error.ModifierNotClosed;
}
diff --git a/src/dom/Element.zig b/src/dom/Element.zig
index ff1a0f2..87fd876 100644
--- a/src/dom/Element.zig
+++ b/src/dom/Element.zig
@@ -9,6 +9,8 @@ pub const Kind = enum {
literal,
};
+pub const Error = html.Error || Allocator.Error;
+
const Self = @This();
kind: Kind,
@@ -22,7 +24,7 @@ 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) !Self {
+pub fn init(alloc: Allocator, knd: Kind, tag: []const u8) Error!Self {
var v = Self{
.kind = knd,
.arena = .init(alloc),
@@ -46,7 +48,7 @@ pub fn initEmpty(alloc: Allocator) Self {
/// 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) !Self {
+pub fn initLit(alloc: Allocator, literal: []const u8) Error!Self {
var v = Self{
.kind = .literal,
.arena = .init(alloc),
@@ -61,7 +63,7 @@ pub fn initLit(alloc: Allocator, literal: []const u8) !Self {
/// 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) !Self {
+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);
@@ -78,7 +80,7 @@ pub fn deinit(self: *Self) void {
self.arena.deinit();
}
-pub fn render(self: *Self, alloc: Allocator) ![]const u8 {
+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);
@@ -109,7 +111,7 @@ pub fn render(self: *Self, alloc: Allocator) ![]const u8 {
return acc.toOwnedSlice(alloc);
}
-fn renderAttribute(self: *Self, alloc: Allocator) !?[]const u8 {
+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);
@@ -131,7 +133,7 @@ fn renderAttribute(self: *Self, alloc: Allocator) !?[]const u8 {
return try acc.toOwnedSlice(alloc);
}
-fn renderClass(self: *const Self, alloc: Allocator) !?[]const u8 {
+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();
@@ -145,7 +147,7 @@ fn renderClass(self: *const Self, alloc: Allocator) !?[]const u8 {
return try acc.toOwnedSlice(alloc);
}
-pub fn setAttribute(self: *Self, k: []const u8, v: []const u8) !void {
+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));
}
@@ -158,7 +160,7 @@ pub fn hasAttribute(self: *Self, k: []const u8) bool {
return self.attributes.contains(k);
}
-pub fn appendClass(self: *Self, v: []const u8) !void {
+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));
}
@@ -171,31 +173,24 @@ pub fn removeClass(self: *Self, v: []const u8) void {
self.class_list.remove(v);
}
-pub fn appendContent(self: *Self, content: Self) !void {
+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) !Self {
+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) !Self {
+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;
}
-/// Init a paragraph tag with an automatically escaped content.
-pub fn initParagraph(alloc: Allocator, content: []const u8) !Self {
- var el = try init(alloc, .content, "p");
- try el.appendContent(try initLitEscaped(alloc, content));
- return el;
-}
-
fn doTest(alloc: Allocator, el: *Self, exp: []const u8) !void {
const got = try el.render(alloc);
defer alloc.free(got);
@@ -229,7 +224,6 @@ test "content element" {
const alloc = std.testing.allocator;
var p = try init(alloc, .content, "p");
- defer p.deinit();
var content = try initLit(alloc, "hello world");
try p.appendContent(content);
@@ -237,15 +231,10 @@ test "content element" {
try doTest(alloc, &content, "hello world");
try doTest(alloc, &p, "<p>hello world</p>");
- var p_managed = try initParagraph(alloc, "hello world");
- defer p_managed.deinit();
-
- try doTest(alloc, &p_managed, "<p>hello world</p>");
-
var div = try init(alloc, .content, "div");
defer div.deinit();
try div.appendClass("foo-bar");
- try div.appendContent(try initParagraph(alloc, "hello world"));
+ 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
index 1734c60..064ebb1 100644
--- a/src/dom/html.zig
+++ b/src/dom/html.zig
@@ -1,7 +1,9 @@
const std = @import("std");
const eql = std.mem.eql;
-pub fn escape(alloc: std.mem.Allocator, v: []const u8) ![]const u8 {
+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);
diff --git a/src/link.zig b/src/link.zig
index 6da585f..85156dc 100644
--- a/src/link.zig
+++ b/src/link.zig
@@ -3,7 +3,7 @@ const Allocator = std.mem.Allocator;
const eql = std.mem.eql;
const Token = @import("lexer/Token.zig");
const Lexer = @import("lexer/Lexer.zig");
-const Element = @import("dom/Element.zig");
+const Element = @import("Element.zig");
const content = @import("content.zig");
const testing = @import("testing.zig");
const doTest = testing.do;
@@ -14,13 +14,9 @@ pub const Error = error{InvalidLink} || Lexer.Error || content.Error || Allocato
pub fn parse(alloc: Allocator, l: *Lexer) Error!Element {
const data = try parseData(alloc, l);
const second = data.second orelse return data.first.?;
- var in = if (data.first) |first| first else try Element.initLitEscaped(alloc, second);
- errdefer in.deinit();
- var el = try Element.init(alloc, .content, "a");
- errdefer el.deinit();
- try el.appendContent(in);
- try el.setAttribute("href", second);
- return el;
+ var in = if (data.first) |first| first else (try Element.Literal.init(alloc, second)).element();
+ errdefer in.deinit(alloc);
+ return (try Element.Link.init(alloc, in, data.second.?)).element();
}
pub const Data = struct {
@@ -32,11 +28,11 @@ pub fn parseData(alloc: Allocator, l: *Lexer) Error!Data {
const v = l.next().?;
if (v.kind != .link) return Error.InvalidLink;
if (!eql(u8, v.content, "[")) {
- const el = try Element.initLitEscaped(alloc, v.content);
- return .{ .first = el, .second = null };
+ const el = try Element.Literal.init(alloc, v.content);
+ return .{ .first = el.element(), .second = null };
}
- var el = Element.initEmpty(alloc);
- errdefer el.deinit();
+ var el = try Element.Empty.init(alloc);
+ errdefer el.deinit(alloc);
while (l.peek()) |next| {
switch (next.kind) {
.weak_delimiter, .strong_delimiter => return Error.InvalidLink,
@@ -47,7 +43,7 @@ pub fn parseData(alloc: Allocator, l: *Lexer) Error!Data {
},
else => {
const in = try content.parse(alloc, l);
- try el.appendContent(in);
+ try el.content.append(alloc, in);
},
}
}
@@ -55,10 +51,15 @@ pub fn parseData(alloc: Allocator, l: *Lexer) Error!Data {
if (href.kind != .literal) return Error.InvalidLink;
const finisher = l.next() orelse return Error.InvalidLink;
if (!finisher.equals(.link, ")")) return Error.InvalidLink;
- return .{
- .first = if (el.content.items.len > 0) el else null,
+ var res = Data{
+ .first = el.element(),
.second = href.content,
};
+ if (el.content.items.len == 0) {
+ res.first = null;
+ el.deinit(alloc);
+ }
+ return res;
}
test "parse links" {
diff --git a/src/paragraph.zig b/src/paragraph.zig
index 92c3792..8edfa12 100644
--- a/src/paragraph.zig
+++ b/src/paragraph.zig
@@ -2,7 +2,7 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const Token = @import("lexer/Token.zig");
const Lexer = @import("lexer/Lexer.zig");
-const Element = @import("dom/Element.zig");
+const Element = @import("Element.zig");
const parser = @import("parser.zig");
const link = @import("link.zig");
const content = @import("content.zig");
@@ -13,45 +13,46 @@ const doTestError = testing.doError;
pub const Error = content.Error || link.Error || Lexer.Error || Allocator.Error;
pub fn parse(alloc: Allocator, l: *Lexer) Error!Element {
- var el = try Element.init(alloc, .content, "p");
- errdefer el.deinit();
+ var el = try Element.Paragraph.init(alloc);
+ errdefer el.deinit(alloc);
while (l.peek()) |next| {
switch (next.kind) {
- // because nextKind returns only an hint for the next rune
- .strong_delimiter => return el,
+ .strong_delimiter => return el.element(),
.weak_delimiter => {
l.consume();
- const future = l.peek() orelse return el;
+ const future = l.peek() orelse return el.element();
switch (future.kind) {
- .literal, .italic, .code, .bold, .link => try el.appendContent(try Element.initLit(alloc, " ")),
- else => return el,
+ .literal, .italic, .code, .bold, .link => {
+ try el.content.append(alloc, (try Element.Literal.init(alloc, " ")).element());
+ },
+ else => return el.element(),
}
},
- else => try el.appendContent(try parseLine(alloc, l)),
+ else => try el.content.append(alloc, try parseLine(alloc, l)),
}
}
- return el;
+ return el.element();
}
pub fn parseLine(alloc: Allocator, l: *Lexer) Error!Element {
- var line = Element.initEmpty(alloc);
- errdefer line.deinit();
+ var line = try Element.Empty.init(alloc);
+ errdefer line.deinit(alloc);
while (l.peek()) |next| {
switch (next.kind) {
- .weak_delimiter, .strong_delimiter => return line,
+ .weak_delimiter, .strong_delimiter => return line.element(),
.link => {
var el = try link.parse(alloc, l);
- errdefer el.deinit();
- try line.appendContent(el);
+ errdefer el.deinit(alloc);
+ try line.content.append(alloc, el);
},
else => {
var el = try content.parse(alloc, l);
- errdefer el.deinit();
- try line.appendContent(el);
+ errdefer el.deinit(alloc);
+ try line.content.append(alloc, el);
},
}
}
- return line;
+ return line.element();
}
test "parse paragraphs" {
diff --git a/src/parser.zig b/src/parser.zig
index dcbf236..639160b 100644
--- a/src/parser.zig
+++ b/src/parser.zig
@@ -2,7 +2,7 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const Token = @import("lexer/Token.zig");
const Lexer = @import("lexer/Lexer.zig");
-const Element = @import("dom/Element.zig");
+const Element = @import("Element.zig");
const paragraph = @import("paragraph.zig");
const title = @import("title.zig");
const link = @import("link.zig");
@@ -46,8 +46,7 @@ fn gen(parent: Allocator, l: *Lexer) Error![]const u8 {
var res = try std.ArrayList(u8).initCapacity(parent, elements.items.len);
errdefer res.deinit(parent);
for (elements.items) |it| {
- var v = it;
- try res.appendSlice(parent, try v.render(alloc));
+ try res.appendSlice(parent, try it.renderHTML(alloc));
}
return res.toOwnedSlice(parent);
}
diff --git a/src/testing.zig b/src/testing.zig
index 8d96808..0911438 100644
--- a/src/testing.zig
+++ b/src/testing.zig
@@ -1,14 +1,14 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const Lexer = @import("lexer/Lexer.zig");
-const Element = @import("dom/Element.zig");
+const Element = @import("Element.zig");
const parser = @import("parser.zig");
pub fn do(comptime parse: fn (Allocator, *Lexer) parser.Error!Element, alloc: Allocator, t: []const u8, v: []const u8) !void {
var l = try Lexer.init(t);
var p = try parse(alloc, &l);
- defer p.deinit();
- const g = try p.render(alloc);
+ defer p.deinit(alloc);
+ const g = try p.renderHTML(alloc);
defer alloc.free(g);
std.testing.expect(std.mem.eql(u8, g, v)) catch |err| {
std.debug.print("{s}\n", .{g});
diff --git a/src/title.zig b/src/title.zig
index dc1113a..506af21 100644
--- a/src/title.zig
+++ b/src/title.zig
@@ -2,7 +2,7 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const Token = @import("lexer/Token.zig");
const Lexer = @import("lexer/Lexer.zig");
-const Element = @import("dom/Element.zig");
+const Element = @import("Element.zig");
const paragraph = @import("paragraph.zig");
const testing = @import("testing.zig");
const doTest = testing.do;
@@ -12,23 +12,14 @@ pub const Error = error{InvalidTitleContent} || paragraph.Error || Lexer.Error;
pub fn parse(alloc: Allocator, l: *Lexer) Error!Element {
const v = l.next().?;
- var el = try Element.init(alloc, .content, switch (v.content.len) {
- 1 => "h1",
- 2 => "h2",
- 3 => "h3",
- 4 => "h4",
- 5 => "h5",
- 6 => "h6",
- else => unreachable,
- });
- errdefer el.deinit();
- try el.appendContent(paragraph.parseLine(alloc, l) catch |err| switch (err) {
+ const el = try Element.Title.init(alloc, @intCast(v.content.len), paragraph.parseLine(alloc, l) catch |err| switch (err) {
paragraph.Error.IllegalPlacement => return Error.InvalidTitleContent,
else => return err,
});
- var next = l.next() orelse return el;
+ errdefer el.deinit(alloc);
+ var next = l.next() orelse return el.element();
if (!next.kind.isDelimiter()) return Error.InvalidTitleContent;
- return el;
+ return el.element();
}
test "parse title" {