feat: add support for allocating extractors

This commit is contained in:
CJ van den Berg 2025-03-22 21:49:19 +01:00
parent 285f64ede6
commit 769bff078f
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9

View file

@ -818,7 +818,11 @@ pub fn match(buf: []const u8, pattern: anytype) Error!bool {
} }
fn extractError(comptime T: type) noreturn { fn extractError(comptime T: type) noreturn {
@compileError("cannot extract type '" ++ @typeName(T) ++ "' from cbor stream"); @compileError("cannot extract type '" ++ @typeName(T) ++ "' from a cbor stream");
}
fn extractErrorAlloc(comptime T: type) noreturn {
@compileError("extracting type '" ++ @typeName(T) ++ "' from a cbor stream requires an allocating extractor, use extractAlloc");
} }
fn hasExtractorTag(info: anytype) bool { fn hasExtractorTag(info: anytype) bool {
@ -837,6 +841,79 @@ fn isExtractor(comptime T: type) bool {
}; };
} }
fn ExtractDef(comptime T: type) type {
return fn (*T, *[]const u8) Error!bool;
}
fn hasExtractMethod(T: type, info: anytype) bool {
const result = blk: {
if (info.is_tuple) break :blk false;
for (info.decls) |decl| {
if (std.mem.eql(u8, decl.name, "cborExtract") and @TypeOf(@field(T, decl.name)) == ExtractDef(T))
break :blk true;
}
break :blk false;
};
// @compileLog("hasExtractMethod", @typeName(T), result);
return result;
}
pub fn isExtractable(comptime T: type) bool {
return comptime switch (@typeInfo(T)) {
.@"struct" => |info| hasExtractMethod(T, info),
.@"enum" => |info| hasExtractMethod(T, info),
.@"union" => |info| hasExtractMethod(T, info),
else => false,
};
}
fn ExtractAllocDef(comptime T: type) type {
return fn (*T, *[]const u8, std.mem.Allocator) Error!bool;
}
fn hasExtractMethodAlloc(T: type, info: anytype) bool {
const result = blk: {
if (@hasField(@TypeOf(info), "is_tuple") and info.is_tuple) break :blk false;
for (info.decls) |decl| {
if (std.mem.eql(u8, decl.name, "cborExtract") and @TypeOf(@field(T, decl.name)) == ExtractAllocDef(T))
break :blk true;
}
break :blk false;
};
// @compileLog("hasExtractMethodAlloc", @typeName(T), result);
return result;
}
pub fn isExtractableAlloc(comptime T: type) bool {
return comptime switch (@typeInfo(T)) {
.@"struct" => |info| hasExtractMethodAlloc(T, info),
.@"enum" => |info| hasExtractMethodAlloc(T, info),
.@"union" => |info| hasExtractMethodAlloc(T, info),
else => false,
};
}
fn GenericExtractorAlloc(T: type) type {
return struct {
dest: *T,
allocator: std.mem.Allocator,
const Self = @This();
pub const EXTRACTOR_TAG = struct {};
pub fn init(dest: *T, allocator: std.mem.Allocator) Self {
return .{ .dest = dest, .allocator = allocator };
}
pub fn extract(self: Self, iter: *[]const u8) Error!bool {
if (comptime isExtractableAlloc(T)) {
return self.dest.cborExtract(iter, self.allocator);
} else {
return self.dest.cborExtract(iter);
}
}
};
}
const JsonValueExtractor = struct { const JsonValueExtractor = struct {
dest: *T, dest: *T,
const Self = @This(); const Self = @This();
@ -914,7 +991,10 @@ fn Extractor(comptime T: type) type {
fn ExtractorType(comptime T: type) type { fn ExtractorType(comptime T: type) type {
const T_type_info = @typeInfo(T); const T_type_info = @typeInfo(T);
if (T_type_info != .pointer) @compileError("extract requires a pointer argument"); if (T_type_info != .pointer) @compileError("extract requires a pointer argument");
return Extractor(T_type_info.pointer.child); return if (isExtractableAlloc(T_type_info.pointer.child))
extractErrorAlloc(T_type_info.pointer.child)
else
Extractor(T_type_info.pointer.child);
} }
pub fn extract(dest: anytype) ExtractorType(@TypeOf(dest)) { pub fn extract(dest: anytype) ExtractorType(@TypeOf(dest)) {
@ -925,6 +1005,21 @@ pub fn extract(dest: anytype) ExtractorType(@TypeOf(dest)) {
return ExtractorType(@TypeOf(dest)).init(dest); return ExtractorType(@TypeOf(dest)).init(dest);
} }
fn ExtractorTypeAlloc(comptime T: type) type {
const T_type_info = @typeInfo(T);
if (T_type_info != .pointer) @compileError("extractAlloc requires a pointer argument");
// @compileLog("ExtractorTypeAlloc", @typeName(T), isExtractableAlloc(T_type_info.pointer.child));
return GenericExtractorAlloc(T_type_info.pointer.child);
}
pub fn extractAlloc(dest: anytype, allocator: std.mem.Allocator) ExtractorTypeAlloc(@TypeOf(dest)) {
comptime {
if (!isExtractor(ExtractorTypeAlloc(@TypeOf(dest))))
@compileError("isExtractor self check failed for " ++ @typeName(ExtractorTypeAlloc(@TypeOf(dest))));
}
return ExtractorTypeAlloc(@TypeOf(dest)).init(dest, allocator);
}
const CborExtractor = struct { const CborExtractor = struct {
dest: *[]const u8, dest: *[]const u8,
const Self = @This(); const Self = @This();