From 164e68e94c99b803cfe516469e0e07c3df8b2288 Mon Sep 17 00:00:00 2001 From: "Lucas F." Date: Sun, 4 Jan 2026 00:39:20 -0300 Subject: [PATCH] update: context --- src/context.zig | 51 +++++++++++++++++++++++++++++ src/context_test.zig | 76 ++++++++++++++++++++++++++++++++++++++++++++ todo.md | 8 ++--- 3 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 src/context.zig create mode 100644 src/context_test.zig diff --git a/src/context.zig b/src/context.zig new file mode 100644 index 0000000..dbb1c5b --- /dev/null +++ b/src/context.zig @@ -0,0 +1,51 @@ +const std = @import("std"); + +pub const Value = union(enum) { + null, + bool: bool, + int: i64, + float: f64, + string: []const u8, + list: []const Value, + dict: std.StringHashMapUnmanaged(Value), + + pub fn deinit(self: Value) void { + _ = self; // nada — a arena libera tudo + } +}; + +pub const Context = struct { + arena: std.heap.ArenaAllocator, + map: std.StringHashMapUnmanaged(Value), + + pub fn init(child_allocator: std.mem.Allocator) Context { + const arena = std.heap.ArenaAllocator.init(child_allocator); + return .{ + .arena = arena, + .map = .{}, + }; + } + + pub fn allocator(self: *Context) std.mem.Allocator { + return self.arena.allocator(); + } + + pub fn deinit(self: *Context) void { + self.arena.deinit(); + } + + pub fn set(self: *Context, key: []const u8, value: Value) !void { + const gop = try self.map.getOrPut(self.allocator(), key); + if (gop.found_existing) { + // opcional: deinit value antigo se necessário + // mas como arena libera tudo, não precisa + } else { + gop.key_ptr.* = try self.allocator().dupe(u8, key); + } + gop.value_ptr.* = value; + } + + pub fn get(self: *const Context, key: []const u8) ?Value { + return self.map.get(key); + } +}; diff --git a/src/context_test.zig b/src/context_test.zig new file mode 100644 index 0000000..8902ac5 --- /dev/null +++ b/src/context_test.zig @@ -0,0 +1,76 @@ +const std = @import("std"); +const testing = std.testing; +const Context = @import("context.zig").Context; +const Value = @import("context.zig").Value; + +test "context com arena" { + const allocator = testing.allocator; + var ctx = Context.init(allocator); + defer ctx.deinit(); + + try ctx.set("nome", Value{ .string = "Lucas" }); // sem dupe! + try ctx.set("idade", Value{ .int = 30 }); + + try testing.expectEqualStrings("Lucas", ctx.get("nome").?.string); + try testing.expectEqual(@as(i64, 30), ctx.get("idade").?.int); +} + +test "context todos os tipos" { + const allocator = testing.allocator; + var ctx = Context.init(allocator); + defer ctx.deinit(); + + // null + try ctx.set("nulo", Value{ .null = {} }); + try testing.expectEqual(ctx.get("nulo").?, .null); + + // bool + try ctx.set("verdadeiro", Value{ .bool = true }); + try ctx.set("falso", Value{ .bool = false }); + try testing.expect(ctx.get("verdadeiro").?.bool == true); + try testing.expect(ctx.get("falso").?.bool == false); + + // int + try ctx.set("idade", Value{ .int = 30 }); + try testing.expect(ctx.get("idade").?.int == 30); + + // float + try ctx.set("preco", Value{ .float = 99.99 }); + try testing.expectApproxEqAbs(@as(f64, 99.99), ctx.get("preco").?.float, 0.001); + + // string + try ctx.set("nome", Value{ .string = "Lucas" }); + try testing.expectEqualStrings("Lucas", ctx.get("nome").?.string); + + // list + var list = try allocator.alloc(Value, 3); + defer allocator.free(list); + + list[0] = Value{ .int = 1 }; + list[1] = Value{ .int = 2 }; + list[2] = Value{ .int = 3 }; + + try ctx.set("numeros", Value{ .list = list }); + const numeros = ctx.get("numeros").?.list; + try testing.expectEqual(@as(usize, 3), numeros.len); + try testing.expect(numeros[0].int == 1); + try testing.expect(numeros[1].int == 2); + try testing.expect(numeros[2].int == 3); + + // dict + var dict = std.StringHashMapUnmanaged(Value){}; + try dict.put(ctx.allocator(), "chave1", Value{ .string = "valor1" }); + try dict.put(ctx.allocator(), "chave2", Value{ .int = 42 }); + try ctx.set("config", Value{ .dict = dict }); + const config = ctx.get("config").?.dict; + try testing.expectEqualStrings("valor1", config.get("chave1").?.string); + try testing.expect(config.get("chave2").?.int == 42); +} + +test "failback" { + const allocator = testing.allocator; + var ctx = Context.init(allocator); + defer ctx.deinit(); + + try testing.expectEqual(ctx.get("nome"), null); +} diff --git a/todo.md b/todo.md index b279286..c7cd2be 100644 --- a/todo.md +++ b/todo.md @@ -22,10 +22,10 @@ - [x] regroup - [x] resetcycle - [x] spaceless -- [ ] templatetag +- [x] templatetag - [x] url - [x] verbatim -- [ ] widthratio +- [x] widthratio - [x] with @@ -114,8 +114,8 @@ ___ - [x] querystring - [x] regroup - [x] resetcycle -- [ ] templatetag -- [ ] widthratio +- [x] templatetag +- [x] widthratio 2 - projetar o Context com calma: - Estrutura hierárquica (escopos aninhados para with, for, etc.)