Compare commits
No commits in common. "main" and "0.1.0" have entirely different histories.
12 changed files with 33 additions and 428 deletions
|
|
@ -84,15 +84,6 @@ pub fn build(b: *std.Build) void {
|
||||||
.use_llvm = true,
|
.use_llvm = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// const lib = b.addLibrary(.{
|
|
||||||
// .name = "zdt_prov",
|
|
||||||
// .root_module = mod,
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// lib.root_module.addIncludePath(b.path("src/svg"));
|
|
||||||
//
|
|
||||||
// b.installArtifact(lib);
|
|
||||||
|
|
||||||
// This declares intent for the executable to be installed into the
|
// This declares intent for the executable to be installed into the
|
||||||
// install prefix when running `zig build` (i.e. when executing the default
|
// install prefix when running `zig build` (i.e. when executing the default
|
||||||
// step). By default the install prefix is `zig-out/` but can be overridden
|
// step). By default the install prefix is `zig-out/` but can be overridden
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,11 @@ const std = @import("std");
|
||||||
const Allocator = std.heap.ArenaAllocator;
|
const Allocator = std.heap.ArenaAllocator;
|
||||||
|
|
||||||
const parser = @import("parser.zig");
|
const parser = @import("parser.zig");
|
||||||
const icons = @import("svg/icons.zig");
|
|
||||||
|
|
||||||
pub const TemplateCache = struct {
|
pub const TemplateCache = struct {
|
||||||
arena: Allocator,
|
arena: Allocator,
|
||||||
cache: std.StringHashMapUnmanaged([]parser.Node),
|
cache: std.StringHashMapUnmanaged([]parser.Node),
|
||||||
default_path: ?[]const u8 = "templates",
|
default_path: ?[]const u8 = "templates",
|
||||||
icons: ?icons.SvgIcon =null,
|
|
||||||
|
|
||||||
pub fn init(child_allocator: std.mem.Allocator) TemplateCache {
|
pub fn init(child_allocator: std.mem.Allocator) TemplateCache {
|
||||||
const arena = std.heap.ArenaAllocator.init(child_allocator);
|
const arena = std.heap.ArenaAllocator.init(child_allocator);
|
||||||
|
|
@ -18,6 +16,7 @@ pub const TemplateCache = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn deinit(self: *TemplateCache) void {
|
pub fn deinit(self: *TemplateCache) void {
|
||||||
self.arena.deinit();
|
self.arena.deinit();
|
||||||
}
|
}
|
||||||
|
|
@ -52,10 +51,6 @@ pub const TemplateCache = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initIcons(self: *TemplateCache) !void {
|
|
||||||
self.icons = icons.SvgIcon.init(self.allocator()) catch null;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(self: *TemplateCache) void {
|
pub fn clear(self: *TemplateCache) void {
|
||||||
self.deinit();
|
self.deinit();
|
||||||
self.cache = .{};
|
self.cache = .{};
|
||||||
|
|
|
||||||
|
|
@ -42,18 +42,6 @@ pub const Context = struct {
|
||||||
return Value{ .string = try time.formatDateTime(self.allocator(), value, "Y-m-d H:i:s") };
|
return Value{ .string = try time.formatDateTime(self.allocator(), value, "Y-m-d H:i:s") };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (@typeInfo(T) == .pointer) {
|
|
||||||
if (@typeInfo(T).pointer.size == .slice) {
|
|
||||||
if (@typeInfo(@typeInfo(T).pointer.child) == .@"struct") {
|
|
||||||
var list = try self.allocator().alloc(Value, value.len);
|
|
||||||
for (value, 0..) |item, i| {
|
|
||||||
list[i] = try self.toValue(item);
|
|
||||||
}
|
|
||||||
return Value{ .list = list };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return switch (@typeInfo(T)) {
|
return switch (@typeInfo(T)) {
|
||||||
.bool => Value{ .bool = value },
|
.bool => Value{ .bool = value },
|
||||||
.int, .comptime_int => Value{ .int = @intCast(value) },
|
.int, .comptime_int => Value{ .int = @intCast(value) },
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,6 @@ const Context = @import("context.zig").Context;
|
||||||
const Value = @import("context.zig").Value;
|
const Value = @import("context.zig").Value;
|
||||||
|
|
||||||
test "context set amigável e get com ponto" {
|
test "context set amigável e get com ponto" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
|
||||||
std.debug.print("1 - context set amigável e get com ponto\n", .{});
|
|
||||||
|
|
||||||
const allocator = testing.allocator;
|
const allocator = testing.allocator;
|
||||||
var ctx = Context.init(allocator);
|
var ctx = Context.init(allocator);
|
||||||
defer ctx.deinit();
|
defer ctx.deinit();
|
||||||
|
|
@ -20,26 +17,17 @@ test "context set amigável e get com ponto" {
|
||||||
// struct
|
// struct
|
||||||
const Person = struct { nome: []const u8, idade: i64 };
|
const Person = struct { nome: []const u8, idade: i64 };
|
||||||
const p = Person{ .nome = "Ana", .idade = 25 };
|
const p = Person{ .nome = "Ana", .idade = 25 };
|
||||||
const p2 = Person{ .nome = "Fulana", .idade = 28 };
|
try ctx.set("user", p);
|
||||||
|
|
||||||
const people = [_]Person{ p, p2 };
|
|
||||||
|
|
||||||
// try ctx.set("user", p);
|
|
||||||
try ctx.set("user", people);
|
|
||||||
|
|
||||||
// list
|
// list
|
||||||
const numeros = [_]i64{ 1, 2, 3 };
|
const numeros = [_]i64{ 1, 2, 3 };
|
||||||
try ctx.set("lista", numeros);
|
try ctx.set("lista", numeros);
|
||||||
|
|
||||||
for (ctx.get("user").?.list) |item| {
|
|
||||||
std.debug.print("user {any}\n", .{item.dict.get("nome").?});
|
|
||||||
}
|
|
||||||
|
|
||||||
// acesso
|
// acesso
|
||||||
try testing.expectEqualStrings("Lucas", ctx.get("nome").?.string);
|
try testing.expectEqualStrings("Lucas", ctx.get("nome").?.string);
|
||||||
try testing.expect(ctx.get("idade").?.int == 30);
|
try testing.expect(ctx.get("idade").?.int == 30);
|
||||||
// try testing.expectEqualStrings("Ana", ctx.get("user.nome").?.string);
|
try testing.expectEqualStrings("Ana", ctx.get("user.nome").?.string);
|
||||||
// try testing.expect(ctx.get("user.idade").?.int == 25);
|
try testing.expect(ctx.get("user.idade").?.int == 25);
|
||||||
try testing.expect(ctx.get("lista.1").?.int == 2);
|
try testing.expect(ctx.get("lista.1").?.int == 2);
|
||||||
try testing.expect(ctx.get("vazio").?.string.len == 0);
|
try testing.expect(ctx.get("vazio").?.string.len == 0);
|
||||||
try testing.expect(ctx.get("preco").?.float == 99.99);
|
try testing.expect(ctx.get("preco").?.float == 99.99);
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,6 @@ const Context = @import("context.zig").Context;
|
||||||
const RelativeDelta = @import("delta.zig").RelativeDelta;
|
const RelativeDelta = @import("delta.zig").RelativeDelta;
|
||||||
|
|
||||||
test "relativedelta rigoroso - meses" {
|
test "relativedelta rigoroso - meses" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
|
||||||
std.debug.print("1 - context set amigável e get com ponto\n", .{});
|
|
||||||
|
|
||||||
const a = try Time.parse("2025-03-31");
|
const a = try Time.parse("2025-03-31");
|
||||||
const b = try Time.parse("2025-01-31");
|
const b = try Time.parse("2025-01-31");
|
||||||
const delta = a.subRelative(b);
|
const delta = a.subRelative(b);
|
||||||
|
|
@ -20,9 +17,6 @@ test "relativedelta rigoroso - meses" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "relativedelta - overflow de dia" {
|
test "relativedelta - overflow de dia" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
|
||||||
std.debug.print("2 - relativedelta - overflow de dia\n", .{});
|
|
||||||
|
|
||||||
const jan31 = try Time.parse("2023-01-31");
|
const jan31 = try Time.parse("2023-01-31");
|
||||||
const mar01 = try Time.parse("2023-03-01");
|
const mar01 = try Time.parse("2023-03-01");
|
||||||
|
|
||||||
|
|
@ -33,9 +27,6 @@ test "relativedelta - overflow de dia" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "bissexto: 2021-02-28 - 2020-02-29" {
|
test "bissexto: 2021-02-28 - 2020-02-29" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
|
||||||
std.debug.print("3 - bissexto: 2021-02-28 - 2020-02-29\n", .{});
|
|
||||||
|
|
||||||
const a = try Time.parse("2021-02-28");
|
const a = try Time.parse("2021-02-28");
|
||||||
const b = try Time.parse("2020-02-29");
|
const b = try Time.parse("2020-02-29");
|
||||||
const delta = a.subRelative(b);
|
const delta = a.subRelative(b);
|
||||||
|
|
@ -45,9 +36,6 @@ test "bissexto: 2021-02-28 - 2020-02-29" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "bissexto: 2021-03-01 - 2020-02-29" {
|
test "bissexto: 2021-03-01 - 2020-02-29" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
|
||||||
std.debug.print("4 - bissexto: 2021-03-01 - 2020-02-29\n", .{});
|
|
||||||
|
|
||||||
const a = try Time.parse("2021-03-01");
|
const a = try Time.parse("2021-03-01");
|
||||||
const b = try Time.parse("2020-02-29");
|
const b = try Time.parse("2020-02-29");
|
||||||
const delta = a.subRelative(b);
|
const delta = a.subRelative(b);
|
||||||
|
|
@ -57,9 +45,6 @@ test "bissexto: 2021-03-01 - 2020-02-29" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "bissexto: 2021-02-27 - 2020-02-29" {
|
test "bissexto: 2021-02-27 - 2020-02-29" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
|
||||||
std.debug.print("5 - bissexto: 2021-02-27 - 2020-02-29\n", .{});
|
|
||||||
|
|
||||||
const a = try Time.parse("2021-02-27");
|
const a = try Time.parse("2021-02-27");
|
||||||
const b = try Time.parse("2020-02-29");
|
const b = try Time.parse("2020-02-29");
|
||||||
const delta = a.subRelative(b);
|
const delta = a.subRelative(b);
|
||||||
|
|
@ -69,9 +54,6 @@ test "bissexto: 2021-02-27 - 2020-02-29" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "bissexto reverso: 2020-02-29 - 2021-02-28" {
|
test "bissexto reverso: 2020-02-29 - 2021-02-28" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
|
||||||
std.debug.print("6 - bissexto reverso: 2020-02-29 - 2021-02-28\n", .{});
|
|
||||||
|
|
||||||
const a = try Time.parse("2020-02-29");
|
const a = try Time.parse("2020-02-29");
|
||||||
const b = try Time.parse("2021-02-28");
|
const b = try Time.parse("2021-02-28");
|
||||||
const delta = a.subRelative(b);
|
const delta = a.subRelative(b);
|
||||||
|
|
@ -81,9 +63,6 @@ test "bissexto reverso: 2020-02-29 - 2021-02-28" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "addRelative: anos normais (não bissexto)" {
|
test "addRelative: anos normais (não bissexto)" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
|
||||||
std.debug.print("7 - addRelative: anos normais (não bissexto)\n", .{});
|
|
||||||
|
|
||||||
// 2023 não é bissexto
|
// 2023 não é bissexto
|
||||||
const base = try Time.parse("2023-06-15");
|
const base = try Time.parse("2023-06-15");
|
||||||
const expected = try Time.parse("2026-06-15");
|
const expected = try Time.parse("2026-06-15");
|
||||||
|
|
@ -98,9 +77,6 @@ test "addRelative: anos normais (não bissexto)" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "addRelative: anos normais com overflow de dia" {
|
test "addRelative: anos normais com overflow de dia" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
|
||||||
std.debug.print("8 - addRelative: anos normais com overflow de dia\n", .{});
|
|
||||||
|
|
||||||
// Janeiro 31 + 2 anos → deve ir para 31/jan (2025 não bissexto)
|
// Janeiro 31 + 2 anos → deve ir para 31/jan (2025 não bissexto)
|
||||||
const base = try Time.parse("2023-01-31");
|
const base = try Time.parse("2023-01-31");
|
||||||
const expected = try Time.parse("2025-01-31");
|
const expected = try Time.parse("2025-01-31");
|
||||||
|
|
@ -115,9 +91,6 @@ test "addRelative: anos normais com overflow de dia" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "addRelative: de 29/fev bissexto + 1 ano (vai para 28/fev)" {
|
test "addRelative: de 29/fev bissexto + 1 ano (vai para 28/fev)" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
|
||||||
std.debug.print("9 - addRelative: de 29/fev bissexto + 1 ano (vai para 28/fev)\n", .{});
|
|
||||||
|
|
||||||
// 2020 foi bissexto → +1 ano deve ir para 2021-02-28 (não bissexto)
|
// 2020 foi bissexto → +1 ano deve ir para 2021-02-28 (não bissexto)
|
||||||
const base = try Time.parse("2020-02-29");
|
const base = try Time.parse("2020-02-29");
|
||||||
const expected = try Time.parse("2021-02-28");
|
const expected = try Time.parse("2021-02-28");
|
||||||
|
|
@ -132,9 +105,6 @@ test "addRelative: de 29/fev bissexto + 1 ano (vai para 28/fev)" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "addRelative: de 29/fev bissexto + 4 anos (permanece 29/fev)" {
|
test "addRelative: de 29/fev bissexto + 4 anos (permanece 29/fev)" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
|
||||||
std.debug.print("10 - addRelative: de 29/fev bissexto + 4 anos (permanece 29/fev)\n", .{});
|
|
||||||
|
|
||||||
// 2020 → 2024 (ambos bissextos)
|
// 2020 → 2024 (ambos bissextos)
|
||||||
const base = try Time.parse("2020-02-29");
|
const base = try Time.parse("2020-02-29");
|
||||||
const expected = try Time.parse("2024-02-29");
|
const expected = try Time.parse("2024-02-29");
|
||||||
|
|
@ -149,9 +119,6 @@ test "addRelative: de 29/fev bissexto + 4 anos (permanece 29/fev)" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "addRelative: de 29/fev + 1 ano + 1 mês (vai para março)" {
|
test "addRelative: de 29/fev + 1 ano + 1 mês (vai para março)" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
|
||||||
std.debug.print("11 - addRelative: de 29/fev + 1 ano + 1 mês (vai para março)\n", .{});
|
|
||||||
|
|
||||||
// 2020-02-29 + 1 ano → 2021-02-28 + 1 mês → 2021-03-28
|
// 2020-02-29 + 1 ano → 2021-02-28 + 1 mês → 2021-03-28
|
||||||
const base = try Time.parse("2020-02-29");
|
const base = try Time.parse("2020-02-29");
|
||||||
const expected = try Time.parse("2021-03-28");
|
const expected = try Time.parse("2021-03-28");
|
||||||
|
|
@ -166,9 +133,6 @@ test "addRelative: de 29/fev + 1 ano + 1 mês (vai para março)" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "addRelative: meses com overflow (31 → 28/30)" {
|
test "addRelative: meses com overflow (31 → 28/30)" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
|
||||||
std.debug.print("12 - addRelative: meses com overflow (31 → 28/30)\n", .{});
|
|
||||||
|
|
||||||
const cases = [_]struct { base: []const u8, months: i32, expected: []const u8 }{
|
const cases = [_]struct { base: []const u8, months: i32, expected: []const u8 }{
|
||||||
.{ .base = "2023-01-31", .months = 1, .expected = "2023-02-28" }, // não bissexto
|
.{ .base = "2023-01-31", .months = 1, .expected = "2023-02-28" }, // não bissexto
|
||||||
.{ .base = "2024-01-31", .months = 1, .expected = "2024-02-29" }, // bissexto
|
.{ .base = "2024-01-31", .months = 1, .expected = "2024-02-29" }, // bissexto
|
||||||
|
|
@ -192,9 +156,6 @@ test "addRelative: meses com overflow (31 → 28/30)" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "addRelative: combinação anos + meses + dias (bissexto envolvido)" {
|
test "addRelative: combinação anos + meses + dias (bissexto envolvido)" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
|
||||||
std.debug.print("13 - addRelative: combinação anos + meses + dias (bissexto envolvido)\n", .{});
|
|
||||||
|
|
||||||
// 2024-02-29 + 1 ano + 2 meses + 3 dias
|
// 2024-02-29 + 1 ano + 2 meses + 3 dias
|
||||||
// → 2025-02-28 + 2 meses → 2025-04-28 + 3 dias → 2025-05-01
|
// → 2025-02-28 + 2 meses → 2025-04-28 + 3 dias → 2025-05-01
|
||||||
const base = try Time.parse("2024-02-29");
|
const base = try Time.parse("2024-02-29");
|
||||||
|
|
@ -210,9 +171,6 @@ test "addRelative: combinação anos + meses + dias (bissexto envolvido)" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "addRelative: delta zero não altera data" {
|
test "addRelative: delta zero não altera data" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
|
||||||
std.debug.print("14 - addRelative: delta zero não altera data\n", .{});
|
|
||||||
|
|
||||||
const base = try Time.parse("2025-07-20");
|
const base = try Time.parse("2025-07-20");
|
||||||
const delta = RelativeDelta.init(.{});
|
const delta = RelativeDelta.init(.{});
|
||||||
const result = base.addRelative(delta);
|
const result = base.addRelative(delta);
|
||||||
|
|
|
||||||
102
src/parser.zig
102
src/parser.zig
|
|
@ -45,7 +45,6 @@ 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,
|
||||||
|
|
@ -89,7 +88,6 @@ pub const TagKind = enum {
|
||||||
resetcycle,
|
resetcycle,
|
||||||
spaceless,
|
spaceless,
|
||||||
super,
|
super,
|
||||||
svg,
|
|
||||||
templatetag,
|
templatetag,
|
||||||
url,
|
url,
|
||||||
verbatim,
|
verbatim,
|
||||||
|
|
@ -137,7 +135,6 @@ 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;
|
||||||
|
|
@ -274,11 +271,6 @@ 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,
|
||||||
|
|
@ -387,10 +379,6 @@ 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);
|
||||||
},
|
},
|
||||||
|
|
@ -709,23 +697,6 @@ 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);
|
||||||
|
|
@ -960,7 +931,7 @@ pub const Node = struct {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
else => {},
|
else => unreachable,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -1696,19 +1667,6 @@ pub const Parser = struct {
|
||||||
current_body = &false_body;
|
current_body = &false_body;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tag: Node = tag_node;
|
|
||||||
if (tag_node.tag.?.kind == .comment) {
|
|
||||||
try self.parseComment();
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
if (try self.parseTagContent(allocator, tag_node)) |tn| {
|
|
||||||
tag.tag.?.body = tn;
|
|
||||||
try current_body.append(allocator, tag);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Qualquer outra tag
|
// Qualquer outra tag
|
||||||
try current_body.append(allocator, tag_node);
|
try current_body.append(allocator, tag_node);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1776,18 +1734,6 @@ pub const Parser = struct {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tag: Node = tag_node;
|
|
||||||
if (tag_node.tag.?.kind == .comment) {
|
|
||||||
try self.parseComment();
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
if (try self.parseTagContent(allocator, tag_node)) |tn| {
|
|
||||||
tag.tag.?.body = tn;
|
|
||||||
try body.append(allocator, tag);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try current_body.append(allocator, tag_node);
|
try current_body.append(allocator, tag_node);
|
||||||
} else {
|
} else {
|
||||||
self.advance(1);
|
self.advance(1);
|
||||||
|
|
@ -2142,50 +2088,6 @@ 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);
|
||||||
|
|
@ -2318,7 +2220,7 @@ pub const Parser = struct {
|
||||||
try self.parseComment();
|
try self.parseComment();
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// std.log.debug("Tag: {s}", .{tag.?.tag.?.raw});
|
std.log.debug("Tag: {s}", .{tag.?.tag.?.raw});
|
||||||
if (try self.parseTagContent(allocator, tag.?)) |tn| {
|
if (try self.parseTagContent(allocator, tag.?)) |tn| {
|
||||||
tag.?.tag.?.body = tn;
|
tag.?.tag.?.body = tn;
|
||||||
try list.append(allocator, tag.?);
|
try list.append(allocator, tag.?);
|
||||||
|
|
|
||||||
|
|
@ -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.random == false);
|
try testing.expect(l.format == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 true %}";
|
const template = "{% lorem 5 p html %}";
|
||||||
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.expect(l.random == true);
|
try testing.expectEqualStrings("html", l.format.?);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "parse simple now" {
|
test "parse simple now" {
|
||||||
|
|
@ -1225,22 +1225,6 @@ 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", .{});
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ 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,
|
||||||
|
|
@ -21,7 +20,6 @@ pub const RenderError = error{
|
||||||
Overflow,
|
Overflow,
|
||||||
Unexpected,
|
Unexpected,
|
||||||
UnsupportedExpression,
|
UnsupportedExpression,
|
||||||
// } || FilterError || parser.ParserError || icons.SvgError || std.fs.File.OpenError;
|
|
||||||
} || FilterError || parser.ParserError || std.fs.File.OpenError;
|
} || FilterError || parser.ParserError || std.fs.File.OpenError;
|
||||||
|
|
||||||
pub const Renderer = struct {
|
pub const Renderer = struct {
|
||||||
|
|
@ -84,17 +82,16 @@ 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);
|
||||||
|
|
@ -103,15 +100,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);
|
||||||
}
|
}
|
||||||
|
|
@ -195,15 +192,15 @@ pub const Renderer = struct {
|
||||||
.tag => {
|
.tag => {
|
||||||
switch (node.tag.?.kind) {
|
switch (node.tag.?.kind) {
|
||||||
.if_block => {
|
.if_block => {
|
||||||
const condition = try self.evaluateCondition(alloc, node.tag.?.body.@"if".condition, context);
|
const condition = try self.evaluateCondition(alloc, node.tag.?.body.@"if".condition);
|
||||||
|
|
||||||
if (condition) {
|
if (condition) {
|
||||||
for (node.tag.?.body.@"if".true_body) |child| {
|
for (node.tag.?.body.@"if".true_body) |child| {
|
||||||
try self.renderNode(alloc, nodes, child, writer, context, null);
|
try self.renderNode(alloc, nodes, child, writer, null, null);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (node.tag.?.body.@"if".false_body) |child| {
|
for (node.tag.?.body.@"if".false_body) |child| {
|
||||||
try self.renderNode(alloc, nodes, child, writer, context, null);
|
try self.renderNode(alloc, nodes, child, writer, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -230,31 +227,11 @@ pub const Renderer = struct {
|
||||||
else => return,
|
else => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (list, 0..) |item, i| {
|
for (list) |item| {
|
||||||
var ctx = Context.init(alloc);
|
var ctx = Context.init(alloc);
|
||||||
defer ctx.deinit();
|
defer ctx.deinit();
|
||||||
|
|
||||||
try ctx.set(node.tag.?.body.@"for".loop_var, item);
|
try ctx.set(node.tag.?.body.@"for".loop_var, item);
|
||||||
try ctx.set("forloop.counter", i + 1);
|
|
||||||
try ctx.set("forloop.counter0", i);
|
|
||||||
try ctx.set("forloop.revcounter", (list.len - i));
|
|
||||||
try ctx.set("forloop.revcounter0", (list.len - i) - 1);
|
|
||||||
try ctx.set("forloop.first", i == 0);
|
|
||||||
try ctx.set("forloop.last", i == (list.len - 1));
|
|
||||||
try ctx.set("forloop.length", list.len);
|
|
||||||
// forloop.counter
|
|
||||||
// The current iteration of the loop (1-indexed)
|
|
||||||
// forloop.counter0
|
|
||||||
// The current iteration of the loop (0-indexed)
|
|
||||||
// forloop.revcounter
|
|
||||||
// The number of iterations from the end of the loop (1-indexed)
|
|
||||||
// forloop.revcounter0
|
|
||||||
// The number of iterations from the end of the loop (0-indexed)
|
|
||||||
// forloop.first
|
|
||||||
// True if this is the first time through the loop
|
|
||||||
// forloop.last
|
|
||||||
// True if this is the last time through the loop
|
|
||||||
// forloop.length
|
|
||||||
|
|
||||||
for (node.tag.?.body.@"for".body) |child| {
|
for (node.tag.?.body.@"for".body) |child| {
|
||||||
try self.renderNode(alloc, nodes, child, writer, &ctx, null);
|
try self.renderNode(alloc, nodes, child, writer, &ctx, null);
|
||||||
|
|
@ -380,7 +357,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;
|
||||||
}
|
}
|
||||||
|
|
@ -397,28 +374,7 @@ pub const Renderer = struct {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.svg => {
|
else => {},
|
||||||
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(" -->");
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -477,7 +433,7 @@ pub const Renderer = struct {
|
||||||
_ = self;
|
_ = self;
|
||||||
var w = buf.writer(alloc);
|
var w = buf.writer(alloc);
|
||||||
switch (value) {
|
switch (value) {
|
||||||
.null => try w.writeAll(""),
|
.null => try w.writeAll("null"),
|
||||||
.bool => |b| try w.print("{}", .{b}),
|
.bool => |b| try w.print("{}", .{b}),
|
||||||
.int => |n| try w.print("{d}", .{n}),
|
.int => |n| try w.print("{d}", .{n}),
|
||||||
.float => |f| try w.print("{d}", .{f}),
|
.float => |f| try w.print("{d}", .{f}),
|
||||||
|
|
@ -500,7 +456,7 @@ pub const Renderer = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evaluateCondition(self: *const Renderer, allocator: Allocator, expr: []const u8, context: ?*Context) RenderError!bool {
|
fn evaluateCondition(self: *const Renderer, allocator: Allocator, expr: []const u8) RenderError!bool {
|
||||||
const trimmed = std.mem.trim(u8, expr, " \t\r\n");
|
const trimmed = std.mem.trim(u8, expr, " \t\r\n");
|
||||||
if (trimmed.len == 0) return false;
|
if (trimmed.len == 0) return false;
|
||||||
|
|
||||||
|
|
@ -533,12 +489,7 @@ pub const Renderer = struct {
|
||||||
const op = tokens.items[1];
|
const op = tokens.items[1];
|
||||||
const right_str = tokens.items[2];
|
const right_str = tokens.items[2];
|
||||||
|
|
||||||
var left_value: Value = Value.null;
|
const left_value = self.context.get(left) orelse Value.null;
|
||||||
if (context) |ctx| {
|
|
||||||
left_value = ctx.get(left) orelse Value.null;
|
|
||||||
}
|
|
||||||
if (left_value == Value.null) left_value = self.context.get(left) orelse Value.null;
|
|
||||||
|
|
||||||
const right_value = parseLiteral(right_str);
|
const right_value = parseLiteral(right_str);
|
||||||
|
|
||||||
if (std.mem.eql(u8, op, ">")) return compare(left_value, right_value, .gt);
|
if (std.mem.eql(u8, op, ">")) return compare(left_value, right_value, .gt);
|
||||||
|
|
|
||||||
|
|
@ -1068,33 +1068,3 @@ 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);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ pub const cache = @import("cache.zig");
|
||||||
pub const context = @import("context.zig");
|
pub const context = @import("context.zig");
|
||||||
pub const delta = @import("delta.zig");
|
pub const delta = @import("delta.zig");
|
||||||
pub const filters = @import("filters.zig");
|
pub const filters = @import("filters.zig");
|
||||||
pub const icons = @import("svg/icons.zig");
|
|
||||||
pub const lorem = @import("lorem.zig");
|
pub const lorem = @import("lorem.zig");
|
||||||
pub const meta = @import("meta.zig");
|
pub const meta = @import("meta.zig");
|
||||||
pub const parser = @import("parser.zig");
|
pub const parser = @import("parser.zig");
|
||||||
|
|
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub const IconSet = enum {
|
|
||||||
bootstrap,
|
|
||||||
dripicons,
|
|
||||||
hero_outline,
|
|
||||||
hero_solid,
|
|
||||||
material,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const embedded_data = std.EnumMap(IconSet, []const u8).init(.{
|
|
||||||
.bootstrap = @embedFile("bootstrap.svgs.bin"),
|
|
||||||
.dripicons = @embedFile("dripicons.svgs.bin"),
|
|
||||||
.hero_outline = @embedFile("hero_outline.svgs.bin"),
|
|
||||||
.hero_solid = @embedFile("hero_solid.svgs.bin"),
|
|
||||||
.material = @embedFile("material.svgs.bin"),
|
|
||||||
});
|
|
||||||
|
|
||||||
pub const SvgIcon = struct {
|
|
||||||
icon_map: std.StringHashMapUnmanaged([]const u8) = .{},
|
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator) !SvgIcon {
|
|
||||||
var self = SvgIcon{};
|
|
||||||
|
|
||||||
inline for (std.meta.fields(IconSet)) |field| {
|
|
||||||
const set = @field(IconSet, field.name);
|
|
||||||
const data = embedded_data.get(set);
|
|
||||||
|
|
||||||
try self.loadSet(allocator, set, data.?);
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *SvgIcon, allocator: std.mem.Allocator) void {
|
|
||||||
var it = self.icon_map.iterator();
|
|
||||||
while (it.next()) |entry| {
|
|
||||||
allocator.free(entry.key_ptr.*);
|
|
||||||
allocator.free(entry.value_ptr.*);
|
|
||||||
}
|
|
||||||
self.icon_map.deinit(allocator);
|
|
||||||
self.* = .{};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(self: *const SvgIcon, key: []const u8) ?[]const u8 {
|
|
||||||
return self.icon_map.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getIcon(self: *const SvgIcon,allocator: std.mem.Allocator, kind: []const u8, name: []const u8) ?[]const u8 {
|
|
||||||
const key = std.fmt.allocPrint(allocator, "{s}:{s}", .{ kind, name }) catch return null;
|
|
||||||
defer allocator.free(key);
|
|
||||||
return self.icon_map.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn count(self: *const SvgIcon) usize {
|
|
||||||
return self.icon_map.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loadSet(
|
|
||||||
self: *SvgIcon,
|
|
||||||
allocator: std.mem.Allocator,
|
|
||||||
set: IconSet,
|
|
||||||
data: []const u8,
|
|
||||||
) !void {
|
|
||||||
if (data.len < 12) return error.InvalidEmbeddedData;
|
|
||||||
|
|
||||||
var pos: usize = 0;
|
|
||||||
|
|
||||||
const magic = std.mem.readInt(u32, data[pos..][0..4], .little);
|
|
||||||
pos += 4;
|
|
||||||
if (magic != 0x53564749) return error.InvalidMagic;
|
|
||||||
|
|
||||||
const version = std.mem.readInt(u32, data[pos..][0..4], .little);
|
|
||||||
pos += 4;
|
|
||||||
if (version != 1) return error.UnsupportedVersion;
|
|
||||||
|
|
||||||
const num_entries = std.mem.readInt(u32, data[pos..][0..4], .little);
|
|
||||||
pos += 4;
|
|
||||||
|
|
||||||
const prefix = @tagName(set);
|
|
||||||
|
|
||||||
var i: u32 = 0;
|
|
||||||
while (i < num_entries) : (i += 1) {
|
|
||||||
const name_len = std.mem.readInt(u32, data[pos..][0..4], .little);
|
|
||||||
pos += 4;
|
|
||||||
|
|
||||||
if (pos + name_len > data.len) return error.CorruptedNameLength;
|
|
||||||
const name_slice = data[pos .. pos + name_len];
|
|
||||||
pos += name_len;
|
|
||||||
|
|
||||||
const svg_len = std.mem.readInt(u32, data[pos..][0..4], .little);
|
|
||||||
pos += 4;
|
|
||||||
|
|
||||||
if (pos + svg_len > data.len) return error.CorruptedSvgLength;
|
|
||||||
const svg_slice = data[pos .. pos + svg_len];
|
|
||||||
pos += svg_len;
|
|
||||||
|
|
||||||
// Monta a chave com prefixo do set
|
|
||||||
const key = try std.fmt.allocPrint(allocator, "{s}:{s}", .{ prefix, name_slice });
|
|
||||||
|
|
||||||
// Duplica o conteúdo SVG (o map assume ownership)
|
|
||||||
const value = try allocator.dupe(u8, svg_slice);
|
|
||||||
|
|
||||||
// Insere no mapa unmanaged
|
|
||||||
try self.icon_map.put(allocator, key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const fallback_svg =
|
|
||||||
\\<div class="svg-container">
|
|
||||||
\\<svg width="24" height="24" fill="none">
|
|
||||||
\\ <rect width="24" height="24" rx="4" fill="#f0f0f0"/>
|
|
||||||
\\ <text x="12" y="16" font-size="10" text-anchor="middle" fill="#999">?</text>
|
|
||||||
\\</svg>
|
|
||||||
\\</div>
|
|
||||||
;
|
|
||||||
|
|
@ -350,10 +350,6 @@ pub const Time = struct {
|
||||||
return unix(std.time.timestamp());
|
return unix(std.time.timestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn now_offset(offset: i64) Time {
|
|
||||||
return unix(std.time.timestamp() + (offset * std.time.s_per_hour));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn today() Time {
|
pub fn today() Time {
|
||||||
return unix(0).setDate(.today());
|
return unix(0).setDate(.today());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue