From 1181f0fa68e2ab221d7fdabbcb0b75257a462610 Mon Sep 17 00:00:00 2001 From: "Lucas F." Date: Tue, 20 Jan 2026 19:04:08 -0300 Subject: [PATCH] update: svgNode --- src/parser.zig | 75 ++++++++++++++++++++++++++++++++++++++++++- src/parser_test.zig | 22 +++++++++++-- src/renderer.zig | 60 +++++++++++++++++++++++----------- src/renderer_test.zig | 30 +++++++++++++++++ 4 files changed, 165 insertions(+), 22 deletions(-) diff --git a/src/parser.zig b/src/parser.zig index f2f7c3e..c5aef01 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -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); diff --git a/src/parser_test.zig b/src/parser_test.zig index bb515f3..02fef3b 100644 --- a/src/parser_test.zig +++ b/src/parser_test.zig @@ -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", .{}); diff --git a/src/renderer.zig b/src/renderer.zig index d317617..a8a0885 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -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,7 +21,8 @@ pub const RenderError = error{ Overflow, Unexpected, 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 { 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 { + _ = 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); } @@ -357,7 +360,7 @@ pub const Renderer = struct { if (random == false) { try writer.writeAll(lorem.LOREM_COMMON_P); return; - }else { + } else { try writer.writeAll(try lorem.sentence(alloc)); return; } @@ -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("
"); + try writer.writeAll(svg_content); + try writer.writeAll("
"); + } else { + try writer.writeAll(icons.fallback_svg); + // Opcional: log ou comentário de debug + // try writer.print("", .{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(""); + }, } }, } diff --git a/src/renderer_test.zig b/src/renderer_test.zig index 2c5a847..c37560d 100644 --- a/src/renderer_test.zig +++ b/src/renderer_test.zig @@ -1068,3 +1068,33 @@ test "renderer - lorem paragraphs random" { const spaces = std.mem.count(u8, buf.items, "

"); 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, "

") != null); + // const spaces = std.mem.count(u8, buf.items, "

"); + // try testing.expect(spaces == 3); +}