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; // 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(); } fn toValue(self: *Context, value: anytype) !Value { const T = @TypeOf(value); return switch (@typeInfo(T)) { .bool => Value{ .bool = value }, .int, .comptime_int => Value{ .int = @intCast(value) }, .float, .comptime_float => Value{ .float = @floatCast(value) }, .pointer => Value{ .string = try std.fmt.allocPrint(self.allocator(), "{s}", .{value}) }, .@"struct" => blk: { var dict = std.StringHashMapUnmanaged(Value){}; inline for (std.meta.fields(T)) |field| { const field_val = @field(value, field.name); const converted = try self.toValue(field_val); try dict.put(self.allocator(), field.name, converted); } break :blk Value{ .dict = dict }; }, .array => blk: { var list = try self.allocator().alloc(Value, value.len); for (value, 0..) |item, i| { list[i] = try self.toValue(item); } break :blk Value{ .list = list }; }, .optional => if (value) |v| try self.toValue(v) else .null, .null => .null, // CASO ESPECIAL: o valor já é um Value (ex: lista de Value) .@"union" => if (T == Value) value else @compileError("Unsupported union type: " ++ @typeName(T)), else => @compileError("Unsupported type: " ++ @typeName(T)), }; } pub fn set(self: *Context, key: []const u8, value: anytype) !void { const v = try self.toValue(value); const gop = try self.map.getOrPut(self.allocator(), key); if (!gop.found_existing) { gop.key_ptr.* = try self.allocator().dupe(u8, key); } gop.value_ptr.* = v; } //get com suporte a ponto ("user.name", "lista.0") pub fn get(self: *const Context, path: []const u8) ?Value { if (path.len == 0) return null; var current = self.map.get(path); if (current) |v| return v; var remaining = path; var start: usize = 0; while (std.mem.indexOfScalarPos(u8, remaining, start, '.')) |dot_pos| { const key = remaining[start..dot_pos]; current = self.map.get(key); if (current == null) return null; remaining = remaining[dot_pos + 1 ..]; start = 0; current = switch (current.?) { .dict => |d| d.get(remaining), .list => |l| blk: { const index = std.fmt.parseInt(usize, remaining, 10) catch return null; break :blk if (index < l.len) l[index] else null; }, else => return null, }; if (current == null) return null; } return current; } pub fn getOr(self: *Context, path: []const u8, default: anytype) ?Value { return self.get(path) orelse try self.toValue(default); } // pub fn get(self: *const Context, comptime T: type, key: []const u8) !T { // // const opt_value = self.map.get(key) orelse return error.KeyNotFound; // const opt_value = self.getOptional(T, key); // // return switch (opt_value) { // .null => if (T == void or @typeInfo(T) == .optional) null else error.TypeMismatch, // .bool => |b| if (T == bool) b else error.TypeMismatch, // .int => |i| if (@typeInfo(T) == .int or @typeInfo(T) == .comptime_int) @intCast(i) else error.TypeMismatch, // .float => |f| if (@typeInfo(T) == .float or @typeInfo(T) == .comptime_float) @floatCast(f) else error.TypeMismatch, // .string => |s| if (T == []const u8) s else error.TypeMismatch, // .list => |l| if (T == []const Value) l else error.TypeMismatch, // .dict => |d| if (T == std.StringHashMapUnmanaged(Value)) d else error.TypeMismatch, // }; // } // // // Versão opcional que retorna optional // pub fn getOptional(self: *const Context, comptime T: type, key: []const u8) ?T { // return self.get(T, key) catch null; // } };