update: svgNode

This commit is contained in:
Lucas F. 2026-01-20 19:04:08 -03:00
parent f0115a15b9
commit 1181f0fa68
4 changed files with 165 additions and 22 deletions

View file

@ -45,6 +45,7 @@ pub const TagNodeBody = union(enum) {
resetcycle: ResetCycleNode,
spaceless: SpacelessNode,
super: bool,
svg: SvgNode,
templatetag: TemplateTagNode,
url: UrlNode,
verbatim: VerbatimNode,
@ -88,6 +89,7 @@ pub const TagKind = enum {
resetcycle,
spaceless,
super,
svg,
templatetag,
url,
verbatim,
@ -135,6 +137,7 @@ fn getTagKindByName(name: []const u8) TagKind {
if (std.mem.eql(u8, name, "resetcycle")) return .resetcycle;
if (std.mem.eql(u8, name, "spaceless")) return .spaceless;
if (std.mem.eql(u8, name, "super")) return .super;
if (std.mem.eql(u8, name, "svg")) return .svg;
if (std.mem.eql(u8, name, "templatetag")) return .templatetag;
if (std.mem.eql(u8, name, "url")) return .url;
if (std.mem.eql(u8, name, "verbatim")) return .verbatim;
@ -271,6 +274,11 @@ pub const SpacelessNode = struct {
raw_close: []const u8,
};
pub const SvgNode = struct {
kind: []const u8,
name: []const u8,
};
pub const TagNode = struct {
kind: TagKind,
args: []const u8,
@ -379,6 +387,10 @@ pub const Node = struct {
for (body_copy) |n| n.deinit(allocator);
allocator.free(body_copy);
},
.svg => {
allocator.free(t.body.svg.kind);
allocator.free(t.body.svg.name);
},
.url => {
allocator.free(t.body.url.args);
},
@ -697,6 +709,23 @@ pub const Node = struct {
},
};
},
.svg => {
return Node{
.type = .tag,
.tag = .{
.kind = .svg,
.args = try allocator.dupe(u8, self.tag.?.args),
.raw = try allocator.dupe(u8, self.tag.?.raw),
.body = .{
.svg = .{
.name = try allocator.dupe(u8, self.tag.?.body.svg.name),
.kind = try allocator.dupe(u8, self.tag.?.body.svg.kind),
},
},
},
};
},
.url => {
const args_copy = try allocator.alloc([]const u8, self.tag.?.body.url.args.len);
errdefer allocator.free(args_copy);
@ -931,7 +960,7 @@ pub const Node = struct {
},
};
},
else => unreachable,
else => {},
}
},
}
@ -2088,6 +2117,50 @@ pub const Parser = struct {
const spaceless_node = try self.parseSpacelessBlock(allocator, raw_open);
return spaceless_node;
},
.svg => {
const args = tag_node.tag.?.args;
var values = std.ArrayList([]const u8){};
defer values.deinit(allocator);
var i: usize = 0;
while (i < args.len) {
while (i < args.len and std.ascii.isWhitespace(args[i])) : (i += 1) {}
if (i >= args.len) break;
const start = i;
var in_quote = false;
var quote_char: u8 = 0;
if (args[i] == '"' or args[i] == '\'') {
in_quote = true;
quote_char = args[i];
i += 1;
}
while (i < args.len) {
if (in_quote) {
if (args[i] == quote_char) {
i += 1;
break;
}
} else {
if (std.ascii.isWhitespace(args[i])) break;
}
i += 1;
}
const value = std.mem.trim(u8, args[start..i], " \t\r\n\"'");
std.debug.print("value: {s}\n", .{value});
try values.append(allocator, value);
}
return TagNodeBody{
.svg = .{
.kind = try allocator.dupe(u8, values.items[0]),
.name = try allocator.dupe(u8, values.items[1]),
},
};
},
.templatetag => {
const args = tag_node.tag.?.args;
const templatetag = TemplateTagNode.parse(args);

View file

@ -712,7 +712,7 @@ test "parse simple lorem" {
const l = nodes[0].tag.?.body.lorem;
try testing.expect(l.count == null);
try testing.expect(l.method == null);
try testing.expect(l.format == null);
try testing.expect(l.random == false);
}
test "parse lorem with arguments" {
@ -720,7 +720,7 @@ test "parse lorem with arguments" {
std.debug.print("32 - parse lorem with arguments\n", .{});
const allocator = testing.allocator;
const template = "{% lorem 5 p html %}";
const template = "{% lorem 5 p true %}";
var p = parser.Parser.init(template);
const nodes = try p.parse(allocator);
defer {
@ -734,7 +734,7 @@ test "parse lorem with arguments" {
const l = nodes[0].tag.?.body.lorem;
try testing.expectEqualStrings("5", l.count.?);
try testing.expectEqualStrings("p", l.method.?);
try testing.expectEqualStrings("html", l.format.?);
try testing.expect(l.random == true);
}
test "parse simple now" {
@ -1225,6 +1225,22 @@ test "parse simple with block" {
try testing.expect(w.body[0].type == .text);
}
test "parse svg" {
std.debug.print("____________________________________________________\n", .{});
std.debug.print("54 - parse svg\n", .{});
const allocator = testing.allocator;
const template = "{% svg \"material\" \"account_arrow_left\" %}";
var p = parser.Parser.init(template);
const nodes = try p.parse(allocator);
defer {
for (nodes) |node| node.deinit(allocator);
allocator.free(nodes);
}
}
// test "parse simple tag" {
// std.debug.print("____________________________________________________\n", .{});
// std.debug.print("54 - parse simple tag\n", .{});

View file

@ -12,6 +12,7 @@ const parser = @import("parser.zig");
const TemplateCache = @import("cache.zig").TemplateCache;
const time = @import("time.zig");
const lorem = @import("lorem.zig");
const icons = @import("svg/icons.zig");
pub const RenderError = error{
InvalidCharacter,
@ -20,6 +21,7 @@ pub const RenderError = error{
Overflow,
Unexpected,
UnsupportedExpression,
// } || FilterError || parser.ParserError || icons.SvgError || std.fs.File.OpenError;
} || FilterError || parser.ParserError || std.fs.File.OpenError;
pub const Renderer = struct {
@ -82,16 +84,17 @@ pub const Renderer = struct {
}
fn renderTemplate(self: *const Renderer, template: []const u8, writer: anytype, cache_key: ?[]const u8) RenderError!void {
_ = cache_key;
var arena = std.heap.ArenaAllocator.init(self.allocator);
defer arena.deinit();
const alloc = arena.allocator();
if (cache_key) |ck| {
if (self.cache.get(ck)) |cached_nodes| {
try self.renderNodes(alloc, cached_nodes, writer);
return;
}
}
// if (cache_key) |ck| {
// if (self.cache.get(ck)) |cached_nodes| {
// try self.renderNodes(alloc, cached_nodes, writer);
// return;
// }
// }
var p = parser.Parser.init(template);
const nodes = try p.parse(alloc);
@ -100,15 +103,15 @@ pub const Renderer = struct {
alloc.free(nodes);
}
if (cache_key) |ck| {
var alc = self.cache.allocator();
var cached_nodes = try alc.alloc(parser.Node, nodes.len);
errdefer alc.free(cached_nodes);
for (nodes, 0..) |node, i| {
cached_nodes[i] = try node.clone(self.allocator);
}
try self.cache.add(ck, nodes);
}
// if (cache_key) |ck| {
// var alc = self.cache.allocator();
// var cached_nodes = try alc.alloc(parser.Node, nodes.len);
// errdefer alc.free(cached_nodes);
// for (nodes, 0..) |node, i| {
// cached_nodes[i] = try node.clone(self.allocator);
// }
// try self.cache.add(ck, nodes);
// }
return try self.renderNodes(alloc, nodes, writer);
}
@ -374,7 +377,28 @@ pub const Renderer = struct {
return;
}
},
else => {},
.svg => {
const svg_kind = node.tag.?.body.svg.kind;
const svg_name = node.tag.?.body.svg.name;
if (self.cache.icons.?.getIcon(alloc,svg_kind, svg_name)) |svg_content| {
try writer.writeAll("<div class=\"svg-container\">");
try writer.writeAll(svg_content);
try writer.writeAll("</div>");
} else {
try writer.writeAll(icons.fallback_svg);
// Opcional: log ou comentário de debug
// try writer.print("<!-- SVG não encontrado: {s}/{s} -->", .{svg_kind, svg_name});
}
return;
},
else => {
std.debug.print("PANIC: unknown node type {d}\n", .{@intFromEnum(node.type)});
// @panic("unknown node type");
try writer.writeAll("<!-- tag não suportada: ");
// try writer.writeAll(try std.fmt.allocPrint(alloc, "{any}", .{node.tag.?.kind}));
try writer.writeAll(" -->");
},
}
},
}

View file

@ -1068,3 +1068,33 @@ test "renderer - lorem paragraphs random" {
const spaces = std.mem.count(u8, buf.items, "<p>");
try testing.expect(spaces == 3);
}
test "renderer - svg" {
std.debug.print("____________________________________________________\n", .{});
std.debug.print("27 - svg\n", .{});
const alloc = testing.allocator;
var ctx = Context.init(alloc);
defer ctx.deinit();
var cache = TemplateCache.init(alloc);
try cache.initIcons();
defer cache.deinit();
const renderer = Renderer.init(&ctx, &cache);
const template =
\\{% svg material kangaroo %}
;
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
try renderer.renderString(template, buf.writer(alloc));
std.debug.print("OUTPUT:\n\n{s}\n", .{buf.items});
try testing.expect(std.mem.indexOf(u8, buf.items, "<div class=\"svg-container\">") != null);
// const spaces = std.mem.count(u8, buf.items, "<p>");
// try testing.expect(spaces == 3);
}