update: context set
This commit is contained in:
parent
164e68e94c
commit
e5305f85c9
2 changed files with 110 additions and 75 deletions
100
src/context.zig
100
src/context.zig
|
|
@ -10,7 +10,7 @@ pub const Value = union(enum) {
|
|||
dict: std.StringHashMapUnmanaged(Value),
|
||||
|
||||
pub fn deinit(self: Value) void {
|
||||
_ = self; // nada — a arena libera tudo
|
||||
_ = self; // arena libera tudo
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -34,18 +34,94 @@ pub const Context = struct {
|
|||
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;
|
||||
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 = std.fmt.allocPrint(self.allocator(), "{s}", .{value}) catch "" },
|
||||
.@"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,
|
||||
else => @compileError("Unsupported type: " ++ @typeName(T)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get(self: *const Context, key: []const u8) ?Value {
|
||||
return self.map.get(key);
|
||||
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 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;
|
||||
// }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,74 +3,33 @@ const testing = std.testing;
|
|||
const Context = @import("context.zig").Context;
|
||||
const Value = @import("context.zig").Value;
|
||||
|
||||
test "context com arena" {
|
||||
test "context set amigável e get com ponto" {
|
||||
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 ctx.set("nome", "Lucas");
|
||||
try ctx.set("idade", 30);
|
||||
try ctx.set("ativo", true);
|
||||
try ctx.set("preco", 99.99);
|
||||
try ctx.set("vazio", "");
|
||||
|
||||
// struct
|
||||
const Person = struct { nome: []const u8, idade: i64 };
|
||||
const p = Person{ .nome = "Ana", .idade = 25 };
|
||||
try ctx.set("user", p);
|
||||
//
|
||||
// // list
|
||||
const numeros = [_]i64{ 1, 2, 3 };
|
||||
try ctx.set("lista", numeros);
|
||||
|
||||
// acesso
|
||||
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);
|
||||
try testing.expectEqualStrings("Ana", ctx.get("user.nome").?.string);
|
||||
try testing.expect(ctx.get("user.idade").?.int == 25);
|
||||
try testing.expect(ctx.get("lista.1").?.int == 2);
|
||||
try testing.expect(ctx.get("vazio").?.string.len == 0);
|
||||
try testing.expect(ctx.get("preco").?.float == 99.99);
|
||||
try testing.expect(ctx.get("ativo").?.bool == true);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue