142 lines
5.3 KiB
Zig
142 lines
5.3 KiB
Zig
const std = @import("std");
|
|
const time = @import("time.zig");
|
|
const util = @import("util.zig");
|
|
|
|
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();
|
|
}
|
|
|
|
pub fn toValue(self: *Context, value: anytype) !Value {
|
|
const T = @TypeOf(value);
|
|
if (T == time.Time) {
|
|
return Value{ .string = try time.formatDateTime(self.allocator(), value, "Y-m-d H:i:s") };
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
fn isArrayList(value: anytype) bool {
|
|
if (std.mem.startsWith(u8, @typeName(value), "array_list")) return true;
|
|
return false;
|
|
}
|
|
|
|
// 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;
|
|
// }
|
|
};
|