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, resetcycle: ResetCycleNode,
spaceless: SpacelessNode, spaceless: SpacelessNode,
super: bool, super: bool,
svg: SvgNode,
templatetag: TemplateTagNode, templatetag: TemplateTagNode,
url: UrlNode, url: UrlNode,
verbatim: VerbatimNode, verbatim: VerbatimNode,
@ -88,6 +89,7 @@ pub const TagKind = enum {
resetcycle, resetcycle,
spaceless, spaceless,
super, super,
svg,
templatetag, templatetag,
url, url,
verbatim, 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, "resetcycle")) return .resetcycle;
if (std.mem.eql(u8, name, "spaceless")) return .spaceless; if (std.mem.eql(u8, name, "spaceless")) return .spaceless;
if (std.mem.eql(u8, name, "super")) return .super; 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, "templatetag")) return .templatetag;
if (std.mem.eql(u8, name, "url")) return .url; if (std.mem.eql(u8, name, "url")) return .url;
if (std.mem.eql(u8, name, "verbatim")) return .verbatim; if (std.mem.eql(u8, name, "verbatim")) return .verbatim;
@ -271,6 +274,11 @@ pub const SpacelessNode = struct {
raw_close: []const u8, raw_close: []const u8,
}; };
pub const SvgNode = struct {
kind: []const u8,
name: []const u8,
};
pub const TagNode = struct { pub const TagNode = struct {
kind: TagKind, kind: TagKind,
args: []const u8, args: []const u8,
@ -379,6 +387,10 @@ pub const Node = struct {
for (body_copy) |n| n.deinit(allocator); for (body_copy) |n| n.deinit(allocator);
allocator.free(body_copy); allocator.free(body_copy);
}, },
.svg => {
allocator.free(t.body.svg.kind);
allocator.free(t.body.svg.name);
},
.url => { .url => {
allocator.free(t.body.url.args); 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 => { .url => {
const args_copy = try allocator.alloc([]const u8, self.tag.?.body.url.args.len); const args_copy = try allocator.alloc([]const u8, self.tag.?.body.url.args.len);
errdefer allocator.free(args_copy); 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); const spaceless_node = try self.parseSpacelessBlock(allocator, raw_open);
return spaceless_node; 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 => { .templatetag => {
const args = tag_node.tag.?.args; const args = tag_node.tag.?.args;
const templatetag = TemplateTagNode.parse(args); const templatetag = TemplateTagNode.parse(args);

View file

@ -712,7 +712,7 @@ test "parse simple lorem" {
const l = nodes[0].tag.?.body.lorem; const l = nodes[0].tag.?.body.lorem;
try testing.expect(l.count == null); try testing.expect(l.count == null);
try testing.expect(l.method == null); try testing.expect(l.method == null);
try testing.expect(l.format == null); try testing.expect(l.random == false);
} }
test "parse lorem with arguments" { test "parse lorem with arguments" {
@ -720,7 +720,7 @@ test "parse lorem with arguments" {
std.debug.print("32 - parse lorem with arguments\n", .{}); std.debug.print("32 - parse lorem with arguments\n", .{});
const allocator = testing.allocator; const allocator = testing.allocator;
const template = "{% lorem 5 p html %}"; const template = "{% lorem 5 p true %}";
var p = parser.Parser.init(template); var p = parser.Parser.init(template);
const nodes = try p.parse(allocator); const nodes = try p.parse(allocator);
defer { defer {
@ -734,7 +734,7 @@ test "parse lorem with arguments" {
const l = nodes[0].tag.?.body.lorem; const l = nodes[0].tag.?.body.lorem;
try testing.expectEqualStrings("5", l.count.?); try testing.expectEqualStrings("5", l.count.?);
try testing.expectEqualStrings("p", l.method.?); try testing.expectEqualStrings("p", l.method.?);
try testing.expectEqualStrings("html", l.format.?); try testing.expect(l.random == true);
} }
test "parse simple now" { test "parse simple now" {
@ -1225,6 +1225,22 @@ test "parse simple with block" {
try testing.expect(w.body[0].type == .text); 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" { // test "parse simple tag" {
// std.debug.print("____________________________________________________\n", .{}); // std.debug.print("____________________________________________________\n", .{});
// std.debug.print("54 - parse simple tag\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 TemplateCache = @import("cache.zig").TemplateCache;
const time = @import("time.zig"); const time = @import("time.zig");
const lorem = @import("lorem.zig"); const lorem = @import("lorem.zig");
const icons = @import("svg/icons.zig");
pub const RenderError = error{ pub const RenderError = error{
InvalidCharacter, InvalidCharacter,
@ -20,7 +21,8 @@ pub const RenderError = error{
Overflow, Overflow,
Unexpected, Unexpected,
UnsupportedExpression, UnsupportedExpression,
} || FilterError || parser.ParserError || std.fs.File.OpenError; // } || FilterError || parser.ParserError || icons.SvgError || std.fs.File.OpenError;
} || FilterError || parser.ParserError || std.fs.File.OpenError;
pub const Renderer = struct { pub const Renderer = struct {
context: *Context, context: *Context,
@ -82,16 +84,17 @@ pub const Renderer = struct {
} }
fn renderTemplate(self: *const Renderer, template: []const u8, writer: anytype, cache_key: ?[]const u8) RenderError!void { 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); var arena = std.heap.ArenaAllocator.init(self.allocator);
defer arena.deinit(); defer arena.deinit();
const alloc = arena.allocator(); const alloc = arena.allocator();
if (cache_key) |ck| { // if (cache_key) |ck| {
if (self.cache.get(ck)) |cached_nodes| { // if (self.cache.get(ck)) |cached_nodes| {
try self.renderNodes(alloc, cached_nodes, writer); // try self.renderNodes(alloc, cached_nodes, writer);
return; // return;
} // }
} // }
var p = parser.Parser.init(template); var p = parser.Parser.init(template);
const nodes = try p.parse(alloc); const nodes = try p.parse(alloc);
@ -100,15 +103,15 @@ pub const Renderer = struct {
alloc.free(nodes); alloc.free(nodes);
} }
if (cache_key) |ck| { // if (cache_key) |ck| {
var alc = self.cache.allocator(); // var alc = self.cache.allocator();
var cached_nodes = try alc.alloc(parser.Node, nodes.len); // var cached_nodes = try alc.alloc(parser.Node, nodes.len);
errdefer alc.free(cached_nodes); // errdefer alc.free(cached_nodes);
for (nodes, 0..) |node, i| { // for (nodes, 0..) |node, i| {
cached_nodes[i] = try node.clone(self.allocator); // cached_nodes[i] = try node.clone(self.allocator);
} // }
try self.cache.add(ck, nodes); // try self.cache.add(ck, nodes);
} // }
return try self.renderNodes(alloc, nodes, writer); return try self.renderNodes(alloc, nodes, writer);
} }
@ -357,7 +360,7 @@ pub const Renderer = struct {
if (random == false) { if (random == false) {
try writer.writeAll(lorem.LOREM_COMMON_P); try writer.writeAll(lorem.LOREM_COMMON_P);
return; return;
}else { } else {
try writer.writeAll(try lorem.sentence(alloc)); try writer.writeAll(try lorem.sentence(alloc));
return; return;
} }
@ -374,7 +377,28 @@ pub const Renderer = struct {
return; 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>"); const spaces = std.mem.count(u8, buf.items, "<p>");
try testing.expect(spaces == 3); 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);
}