summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Vincent Rischmann2023-11-23 21:43:30 +0100
committerGravatar Vincent Rischmann2023-11-23 21:43:30 +0100
commit123eb09609c09f1187a1d29bd5dbed9e0962ec54 (patch)
tree191c0ac15659665390a6e958a98208dc8d08053a
parentMerge pull request #143 from edyu/master (diff)
parentuse @hasField (diff)
downloadzig-sqlite-123eb09609c09f1187a1d29bd5dbed9e0962ec54.tar.gz
zig-sqlite-123eb09609c09f1187a1d29bd5dbed9e0962ec54.tar.xz
zig-sqlite-123eb09609c09f1187a1d29bd5dbed9e0962ec54.zip
Merge branch 'fix-latest-zig'
-rw-r--r--helpers.zig16
-rw-r--r--sqlite.zig64
-rw-r--r--tools/preprocess_files.zig4
-rw-r--r--vtab.zig49
4 files changed, 98 insertions, 35 deletions
diff --git a/helpers.zig b/helpers.zig
index 1a56231..7bb695e 100644
--- a/helpers.zig
+++ b/helpers.zig
@@ -86,3 +86,19 @@ fn sliceFromValue(sqlite_value: *c.sqlite3_value) []const u8 {
86 86
87 return value[0..size]; 87 return value[0..size];
88} 88}
89
90// Returns true if the type T has a function named `name`.
91pub fn hasFn(comptime T: type, comptime name: []const u8) bool {
92 if (!@hasDecl(T, name)) {
93 return false;
94 }
95
96 const decl = @field(T, name);
97 const decl_type = @TypeOf(decl);
98 const decl_type_info = @typeInfo(decl_type);
99
100 return switch (decl_type_info) {
101 .Fn => true,
102 else => false,
103 };
104}
diff --git a/sqlite.zig b/sqlite.zig
index d521175..8477151 100644
--- a/sqlite.zig
+++ b/sqlite.zig
@@ -21,6 +21,7 @@ const getDetailedErrorFromResultCode = errors.getDetailedErrorFromResultCode;
21 21
22const getTestDb = @import("test.zig").getTestDb; 22const getTestDb = @import("test.zig").getTestDb;
23pub const vtab = @import("vtab.zig"); 23pub const vtab = @import("vtab.zig");
24
24const helpers = @import("helpers.zig"); 25const helpers = @import("helpers.zig");
25 26
26test { 27test {
@@ -29,6 +30,43 @@ test {
29 30
30const logger = std.log.scoped(.sqlite); 31const logger = std.log.scoped(.sqlite);
31 32
33// Returns true if the passed type is a struct.
34fn isStruct(comptime T: type) bool {
35 const type_info = @typeInfo(T);
36 return type_info == .Struct;
37}
38
39// Returns true if the passed type will coerce to []const u8.
40//
41// NOTE(vincent): this is straight from the Zig stdlib before it was removed.
42fn isZigString(comptime T: type) bool {
43 return comptime blk: {
44 // Only pointer types can be strings, no optionals
45 const info = @typeInfo(T);
46 if (info != .Pointer) break :blk false;
47
48 const ptr = &info.Pointer;
49 // Check for CV qualifiers that would prevent coerction to []const u8
50 if (ptr.is_volatile or ptr.is_allowzero) break :blk false;
51
52 // If it's already a slice, simple check.
53 if (ptr.size == .Slice) {
54 break :blk ptr.child == u8;
55 }
56
57 // Otherwise check if it's an array type that coerces to slice.
58 if (ptr.size == .One) {
59 const child = @typeInfo(ptr.child);
60 if (child == .Array) {
61 const arr = &child.Array;
62 break :blk arr.child == u8;
63 }
64 }
65
66 break :blk false;
67 };
68}
69
32/// Text is used to represent a SQLite TEXT value when binding a parameter or reading a column. 70/// Text is used to represent a SQLite TEXT value when binding a parameter or reading a column.
33pub const Text = struct { data: []const u8 }; 71pub const Text = struct { data: []const u8 };
34 72
@@ -1062,7 +1100,7 @@ pub fn Iterator(comptime Type: type) type {
1062 .Enum => |TI| { 1100 .Enum => |TI| {
1063 debug.assert(columns == 1); 1101 debug.assert(columns == 1);
1064 1102
1065 if (comptime std.meta.trait.isZigString(Type.BaseType)) { 1103 if (comptime isZigString(Type.BaseType)) {
1066 @compileError("cannot read into type " ++ @typeName(Type) ++ " ; BaseType " ++ @typeName(Type.BaseType) ++ " requires allocation, use nextAlloc or oneAlloc"); 1104 @compileError("cannot read into type " ++ @typeName(Type) ++ " ; BaseType " ++ @typeName(Type.BaseType) ++ " requires allocation, use nextAlloc or oneAlloc");
1067 } 1105 }
1068 1106
@@ -1144,7 +1182,7 @@ pub fn Iterator(comptime Type: type) type {
1144 1182
1145 const inner_value = try self.readField(Type.BaseType, .{ .allocator = allocator }, 0); 1183 const inner_value = try self.readField(Type.BaseType, .{ .allocator = allocator }, 0);
1146 1184
1147 if (comptime std.meta.trait.isZigString(Type.BaseType)) { 1185 if (comptime isZigString(Type.BaseType)) {
1148 // The inner value is never returned to the user, we must free it ourselves. 1186 // The inner value is never returned to the user, we must free it ourselves.
1149 defer allocator.free(inner_value); 1187 defer allocator.free(inner_value);
1150 1188
@@ -1308,10 +1346,10 @@ pub fn Iterator(comptime Type: type) type {
1308 } 1346 }
1309 1347
1310 fn readPointer(self: *Self, comptime PointerType: type, options: anytype, i: usize) !PointerType { 1348 fn readPointer(self: *Self, comptime PointerType: type, options: anytype, i: usize) !PointerType {
1311 if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) { 1349 if (!comptime isStruct(@TypeOf(options))) {
1312 @compileError("options passed to readPointer must be a struct"); 1350 @compileError("options passed to readPointer must be a struct");
1313 } 1351 }
1314 if (!comptime std.meta.trait.hasField("allocator")(@TypeOf(options))) { 1352 if (!@hasField(@TypeOf(options), "allocator")) {
1315 @compileError("options passed to readPointer must have an allocator field"); 1353 @compileError("options passed to readPointer must have an allocator field");
1316 } 1354 }
1317 1355
@@ -1339,7 +1377,7 @@ pub fn Iterator(comptime Type: type) type {
1339 } 1377 }
1340 1378
1341 fn readOptional(self: *Self, comptime OptionalType: type, options: anytype, _i: usize) !OptionalType { 1379 fn readOptional(self: *Self, comptime OptionalType: type, options: anytype, _i: usize) !OptionalType {
1342 if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) { 1380 if (!comptime isStruct(@TypeOf(options))) {
1343 @compileError("options passed to readOptional must be a struct"); 1381 @compileError("options passed to readOptional must be a struct");
1344 } 1382 }
1345 1383
@@ -1384,7 +1422,7 @@ pub fn Iterator(comptime Type: type) type {
1384 // 1422 //
1385 // TODO(vincent): add comptime checks for the fields/columns. 1423 // TODO(vincent): add comptime checks for the fields/columns.
1386 fn readStruct(self: *Self, options: anytype) !Type { 1424 fn readStruct(self: *Self, options: anytype) !Type {
1387 if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) { 1425 if (!comptime isStruct(@TypeOf(options))) {
1388 @compileError("options passed to readStruct must be a struct"); 1426 @compileError("options passed to readStruct must be a struct");
1389 } 1427 }
1390 1428
@@ -1402,7 +1440,7 @@ pub fn Iterator(comptime Type: type) type {
1402 } 1440 }
1403 1441
1404 fn readField(self: *Self, comptime FieldType: type, options: anytype, i: usize) !FieldType { 1442 fn readField(self: *Self, comptime FieldType: type, options: anytype, i: usize) !FieldType {
1405 if (!comptime std.meta.trait.is(.Struct)(@TypeOf(options))) { 1443 if (!comptime isStruct(@TypeOf(options))) {
1406 @compileError("options passed to readField must be a struct"); 1444 @compileError("options passed to readField must be a struct");
1407 } 1445 }
1408 1446
@@ -1410,13 +1448,13 @@ pub fn Iterator(comptime Type: type) type {
1410 1448
1411 return switch (FieldType) { 1449 return switch (FieldType) {
1412 Blob => blk: { 1450 Blob => blk: {
1413 if (!comptime std.meta.trait.hasField("allocator")(@TypeOf(options))) { 1451 if (!@hasField(@TypeOf(options), "allocator")) {
1414 @compileError("options passed to readPointer must have an allocator field when reading a Blob"); 1452 @compileError("options passed to readPointer must have an allocator field when reading a Blob");
1415 } 1453 }
1416 break :blk try self.readBytes(Blob, options.allocator, i, .Blob); 1454 break :blk try self.readBytes(Blob, options.allocator, i, .Blob);
1417 }, 1455 },
1418 Text => blk: { 1456 Text => blk: {
1419 if (!comptime std.meta.trait.hasField("allocator")(@TypeOf(options))) { 1457 if (!@hasField(@TypeOf(options), "allocator")) {
1420 @compileError("options passed to readField must have an allocator field when reading a Text"); 1458 @compileError("options passed to readField must have an allocator field when reading a Text");
1421 } 1459 }
1422 break :blk try self.readBytes(Text, options.allocator, i, .Text); 1460 break :blk try self.readBytes(Text, options.allocator, i, .Text);
@@ -1432,7 +1470,7 @@ pub fn Iterator(comptime Type: type) type {
1432 .Enum => |TI| { 1470 .Enum => |TI| {
1433 const inner_value = try self.readField(FieldType.BaseType, options, i); 1471 const inner_value = try self.readField(FieldType.BaseType, options, i);
1434 1472
1435 if (comptime std.meta.trait.isZigString(FieldType.BaseType)) { 1473 if (comptime isZigString(FieldType.BaseType)) {
1436 // The inner value is never returned to the user, we must free it ourselves. 1474 // The inner value is never returned to the user, we must free it ourselves.
1437 defer options.allocator.free(inner_value); 1475 defer options.allocator.free(inner_value);
1438 1476
@@ -1642,7 +1680,7 @@ pub const DynamicStatement = struct {
1642 return convertResultToError(result); 1680 return convertResultToError(result);
1643 }, 1681 },
1644 .Enum => { 1682 .Enum => {
1645 if (comptime std.meta.trait.isZigString(FieldType.BaseType)) { 1683 if (comptime isZigString(FieldType.BaseType)) {
1646 try self.bindField(FieldType.BaseType, options, field_name, i, @tagName(field)); 1684 try self.bindField(FieldType.BaseType, options, field_name, i, @tagName(field));
1647 } else if (@typeInfo(FieldType.BaseType) == .Int) { 1685 } else if (@typeInfo(FieldType.BaseType) == .Int) {
1648 try self.bindField(FieldType.BaseType, options, field_name, i, @intFromEnum(field)); 1686 try self.bindField(FieldType.BaseType, options, field_name, i, @intFromEnum(field));
@@ -1655,7 +1693,7 @@ pub const DynamicStatement = struct {
1655 try self.bindField(info.backing_integer.?, options, field_name, i, @as(info.backing_integer.?, @bitCast(field))); 1693 try self.bindField(info.backing_integer.?, options, field_name, i, @as(info.backing_integer.?, @bitCast(field)));
1656 return; 1694 return;
1657 } 1695 }
1658 if (!comptime std.meta.trait.hasFn("bindField")(FieldType)) { 1696 if (!comptime helpers.hasFn(FieldType, "bindField")) {
1659 @compileError("cannot bind field " ++ field_name ++ " of type " ++ @typeName(FieldType) ++ ", consider implementing the bindField() method"); 1697 @compileError("cannot bind field " ++ field_name ++ " of type " ++ @typeName(FieldType) ++ ", consider implementing the bindField() method");
1660 } 1698 }
1661 1699
@@ -2013,7 +2051,7 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: anytype) type
2013 /// The types are checked at comptime. 2051 /// The types are checked at comptime.
2014 fn bind(self: *Self, options: anytype, values: anytype) !void { 2052 fn bind(self: *Self, options: anytype, values: anytype) !void {
2015 const StructType = @TypeOf(values); 2053 const StructType = @TypeOf(values);
2016 if (!comptime std.meta.trait.is(.Struct)(@TypeOf(values))) { 2054 if (!comptime isStruct(StructType)) {
2017 @compileError("options passed to Statement.bind must be a struct (DynamicStatement supports runtime slices)"); 2055 @compileError("options passed to Statement.bind must be a struct (DynamicStatement supports runtime slices)");
2018 } 2056 }
2019 2057
diff --git a/tools/preprocess_files.zig b/tools/preprocess_files.zig
index 3a54a93..2bb8eb7 100644
--- a/tools/preprocess_files.zig
+++ b/tools/preprocess_files.zig
@@ -162,7 +162,7 @@ fn preprocessSqlite3HeaderFile(gpa: mem.Allocator) !void {
162 162
163 // 163 //
164 164
165 var data = try readOriginalData(allocator, "c/sqlite3.h"); 165 const data = try readOriginalData(allocator, "c/sqlite3.h");
166 166
167 var processor = try Processor.init(allocator, data); 167 var processor = try Processor.init(allocator, data);
168 168
@@ -212,7 +212,7 @@ fn preprocessSqlite3ExtHeaderFile(gpa: mem.Allocator) !void {
212 212
213 // 213 //
214 214
215 var data = try readOriginalData(allocator, "c/sqlite3ext.h"); 215 const data = try readOriginalData(allocator, "c/sqlite3ext.h");
216 216
217 var processor = try Processor.init(allocator, data); 217 var processor = try Processor.init(allocator, data);
218 218
diff --git a/vtab.zig b/vtab.zig
index 6fbd865..eb73990 100644
--- a/vtab.zig
+++ b/vtab.zig
@@ -16,6 +16,15 @@ const helpers = @import("helpers.zig");
16 16
17const logger = std.log.scoped(.vtab); 17const logger = std.log.scoped(.vtab);
18 18
19fn hasDecls(comptime T: type, comptime names: anytype) bool {
20 inline for (names) |name| {
21 if (!@hasDecl(T, name)) {
22 return false;
23 }
24 }
25 return true;
26}
27
19/// ModuleContext contains state that is needed by all implementations of virtual tables. 28/// ModuleContext contains state that is needed by all implementations of virtual tables.
20/// 29///
21/// Currently there's only an allocator. 30/// Currently there's only an allocator.
@@ -301,7 +310,7 @@ fn validateCursorType(comptime Table: type) void {
301 310
302 // Validate the `init` function 311 // Validate the `init` function
303 { 312 {
304 if (!meta.trait.hasDecls(Cursor, .{"InitError"})) { 313 if (!comptime hasDecls(Cursor, .{"InitError"})) {
305 @compileError("the Cursor type must declare a InitError error set for the init function"); 314 @compileError("the Cursor type must declare a InitError error set for the init function");
306 } 315 }
307 316
@@ -309,7 +318,7 @@ fn validateCursorType(comptime Table: type) void {
309 \\the Cursor.init function must have the signature `fn init(allocator: std.mem.Allocator, parent: *Table) InitError!*Cursor` 318 \\the Cursor.init function must have the signature `fn init(allocator: std.mem.Allocator, parent: *Table) InitError!*Cursor`
310 ; 319 ;
311 320
312 if (!meta.trait.hasFn("init")(Cursor)) { 321 if (!comptime helpers.hasFn(Cursor, "init")) {
313 @compileError("the Cursor type must have an init function, " ++ error_message); 322 @compileError("the Cursor type must have an init function, " ++ error_message);
314 } 323 }
315 324
@@ -327,7 +336,7 @@ fn validateCursorType(comptime Table: type) void {
327 \\the Cursor.deinit function must have the signature `fn deinit(cursor: *Cursor) void` 336 \\the Cursor.deinit function must have the signature `fn deinit(cursor: *Cursor) void`
328 ; 337 ;
329 338
330 if (!meta.trait.hasFn("deinit")(Cursor)) { 339 if (!comptime helpers.hasFn(Cursor, "deinit")) {
331 @compileError("the Cursor type must have a deinit function, " ++ error_message); 340 @compileError("the Cursor type must have a deinit function, " ++ error_message);
332 } 341 }
333 342
@@ -340,7 +349,7 @@ fn validateCursorType(comptime Table: type) void {
340 349
341 // Validate the `next` function 350 // Validate the `next` function
342 { 351 {
343 if (!meta.trait.hasDecls(Cursor, .{"NextError"})) { 352 if (!comptime hasDecls(Cursor, .{"NextError"})) {
344 @compileError("the Cursor type must declare a NextError error set for the next function"); 353 @compileError("the Cursor type must declare a NextError error set for the next function");
345 } 354 }
346 355
@@ -348,7 +357,7 @@ fn validateCursorType(comptime Table: type) void {
348 \\the Cursor.next function must have the signature `fn next(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics) NextError!void` 357 \\the Cursor.next function must have the signature `fn next(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics) NextError!void`
349 ; 358 ;
350 359
351 if (!meta.trait.hasFn("next")(Cursor)) { 360 if (!comptime helpers.hasFn(Cursor, "next")) {
352 @compileError("the Cursor type must have a next function, " ++ error_message); 361 @compileError("the Cursor type must have a next function, " ++ error_message);
353 } 362 }
354 363
@@ -362,7 +371,7 @@ fn validateCursorType(comptime Table: type) void {
362 371
363 // Validate the `hasNext` function 372 // Validate the `hasNext` function
364 { 373 {
365 if (!meta.trait.hasDecls(Cursor, .{"HasNextError"})) { 374 if (!comptime hasDecls(Cursor, .{"HasNextError"})) {
366 @compileError("the Cursor type must declare a HasNextError error set for the hasNext function"); 375 @compileError("the Cursor type must declare a HasNextError error set for the hasNext function");
367 } 376 }
368 377
@@ -370,7 +379,7 @@ fn validateCursorType(comptime Table: type) void {
370 \\the Cursor.hasNext function must have the signature `fn hasNext(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics) HasNextError!bool` 379 \\the Cursor.hasNext function must have the signature `fn hasNext(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics) HasNextError!bool`
371 ; 380 ;
372 381
373 if (!meta.trait.hasFn("hasNext")(Cursor)) { 382 if (!comptime helpers.hasFn(Cursor, "hasNext")) {
374 @compileError("the Cursor type must have a hasNext function, " ++ error_message); 383 @compileError("the Cursor type must have a hasNext function, " ++ error_message);
375 } 384 }
376 385
@@ -384,7 +393,7 @@ fn validateCursorType(comptime Table: type) void {
384 393
385 // Validate the `filter` function 394 // Validate the `filter` function
386 { 395 {
387 if (!meta.trait.hasDecls(Cursor, .{"FilterError"})) { 396 if (!comptime hasDecls(Cursor, .{"FilterError"})) {
388 @compileError("the Cursor type must declare a FilterError error set for the filter function"); 397 @compileError("the Cursor type must declare a FilterError error set for the filter function");
389 } 398 }
390 399
@@ -392,7 +401,7 @@ fn validateCursorType(comptime Table: type) void {
392 \\the Cursor.filter function must have the signature `fn filter(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics, index: sqlite.vtab.IndexIdentifier, args: []FilterArg) FilterError!bool` 401 \\the Cursor.filter function must have the signature `fn filter(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics, index: sqlite.vtab.IndexIdentifier, args: []FilterArg) FilterError!bool`
393 ; 402 ;
394 403
395 if (!meta.trait.hasFn("filter")(Cursor)) { 404 if (!comptime helpers.hasFn(Cursor, "filter")) {
396 @compileError("the Cursor type must have a filter function, " ++ error_message); 405 @compileError("the Cursor type must have a filter function, " ++ error_message);
397 } 406 }
398 407
@@ -408,10 +417,10 @@ fn validateCursorType(comptime Table: type) void {
408 417
409 // Validate the `column` function 418 // Validate the `column` function
410 { 419 {
411 if (!meta.trait.hasDecls(Cursor, .{"ColumnError"})) { 420 if (!comptime hasDecls(Cursor, .{"ColumnError"})) {
412 @compileError("the Cursor type must declare a ColumnError error set for the column function"); 421 @compileError("the Cursor type must declare a ColumnError error set for the column function");
413 } 422 }
414 if (!meta.trait.hasDecls(Cursor, .{"Column"})) { 423 if (!comptime hasDecls(Cursor, .{"Column"})) {
415 @compileError("the Cursor type must declare a Column type for the return type of the column function"); 424 @compileError("the Cursor type must declare a Column type for the return type of the column function");
416 } 425 }
417 426
@@ -419,7 +428,7 @@ fn validateCursorType(comptime Table: type) void {
419 \\the Cursor.column function must have the signature `fn column(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics, column_number: i32) ColumnError!Column` 428 \\the Cursor.column function must have the signature `fn column(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics, column_number: i32) ColumnError!Column`
420 ; 429 ;
421 430
422 if (!meta.trait.hasFn("column")(Cursor)) { 431 if (!comptime helpers.hasFn(Cursor, "column")) {
423 @compileError("the Cursor type must have a column function, " ++ error_message); 432 @compileError("the Cursor type must have a column function, " ++ error_message);
424 } 433 }
425 434
@@ -434,7 +443,7 @@ fn validateCursorType(comptime Table: type) void {
434 443
435 // Validate the `rowId` function 444 // Validate the `rowId` function
436 { 445 {
437 if (!meta.trait.hasDecls(Cursor, .{"RowIDError"})) { 446 if (!comptime hasDecls(Cursor, .{"RowIDError"})) {
438 @compileError("the Cursor type must declare a RowIDError error set for the rowId function"); 447 @compileError("the Cursor type must declare a RowIDError error set for the rowId function");
439 } 448 }
440 449
@@ -442,7 +451,7 @@ fn validateCursorType(comptime Table: type) void {
442 \\the Cursor.rowId function must have the signature `fn rowId(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics) RowIDError!i64` 451 \\the Cursor.rowId function must have the signature `fn rowId(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics) RowIDError!i64`
443 ; 452 ;
444 453
445 if (!meta.trait.hasFn("rowId")(Cursor)) { 454 if (!comptime helpers.hasFn(Cursor, "rowId")) {
446 @compileError("the Cursor type must have a rowId function, " ++ error_message); 455 @compileError("the Cursor type must have a rowId function, " ++ error_message);
447 } 456 }
448 457
@@ -459,7 +468,7 @@ fn validateCursorType(comptime Table: type) void {
459fn validateTableType(comptime Table: type) void { 468fn validateTableType(comptime Table: type) void {
460 // Validate the `init` function 469 // Validate the `init` function
461 { 470 {
462 if (!meta.trait.hasDecls(Table, .{"InitError"})) { 471 if (!comptime hasDecls(Table, .{"InitError"})) {
463 @compileError("the Table type must declare a InitError error set for the init function"); 472 @compileError("the Table type must declare a InitError error set for the init function");
464 } 473 }
465 474
@@ -467,7 +476,7 @@ fn validateTableType(comptime Table: type) void {
467 \\the Table.init function must have the signature `fn init(allocator: std.mem.Allocator, diags: *sqlite.vtab.VTabDiagnostics, args: []const ModuleArgument) InitError!*Table` 476 \\the Table.init function must have the signature `fn init(allocator: std.mem.Allocator, diags: *sqlite.vtab.VTabDiagnostics, args: []const ModuleArgument) InitError!*Table`
468 ; 477 ;
469 478
470 if (!meta.trait.hasFn("init")(Table)) { 479 if (!comptime helpers.hasFn(Table, "init")) {
471 @compileError("the Table type must have a init function, " ++ error_message); 480 @compileError("the Table type must have a init function, " ++ error_message);
472 } 481 }
473 482
@@ -487,7 +496,7 @@ fn validateTableType(comptime Table: type) void {
487 \\the Table.deinit function must have the signature `fn deinit(table: *Table, allocator: std.mem.Allocator) void` 496 \\the Table.deinit function must have the signature `fn deinit(table: *Table, allocator: std.mem.Allocator) void`
488 ; 497 ;
489 498
490 if (!meta.trait.hasFn("deinit")(Table)) { 499 if (!comptime helpers.hasFn(Table, "deinit")) {
491 @compileError("the Table type must have a deinit function, " ++ error_message); 500 @compileError("the Table type must have a deinit function, " ++ error_message);
492 } 501 }
493 502
@@ -501,7 +510,7 @@ fn validateTableType(comptime Table: type) void {
501 510
502 // Validate the `buildBestIndex` function 511 // Validate the `buildBestIndex` function
503 { 512 {
504 if (!meta.trait.hasDecls(Table, .{"BuildBestIndexError"})) { 513 if (!comptime hasDecls(Table, .{"BuildBestIndexError"})) {
505 @compileError("the Cursor type must declare a BuildBestIndexError error set for the buildBestIndex function"); 514 @compileError("the Cursor type must declare a BuildBestIndexError error set for the buildBestIndex function");
506 } 515 }
507 516
@@ -509,7 +518,7 @@ fn validateTableType(comptime Table: type) void {
509 \\the Table.buildBestIndex function must have the signature `fn buildBestIndex(table: *Table, diags: *sqlite.vtab.VTabDiagnostics, builder: *sqlite.vtab.BestIndexBuilder) BuildBestIndexError!void` 518 \\the Table.buildBestIndex function must have the signature `fn buildBestIndex(table: *Table, diags: *sqlite.vtab.VTabDiagnostics, builder: *sqlite.vtab.BestIndexBuilder) BuildBestIndexError!void`
510 ; 519 ;
511 520
512 if (!meta.trait.hasFn("buildBestIndex")(Table)) { 521 if (!comptime helpers.hasFn(Table, "buildBestIndex")) {
513 @compileError("the Table type must have a buildBestIndex function, " ++ error_message); 522 @compileError("the Table type must have a buildBestIndex function, " ++ error_message);
514 } 523 }
515 524
@@ -522,7 +531,7 @@ fn validateTableType(comptime Table: type) void {
522 if (info.return_type.? != Table.BuildBestIndexError!void) @compileError(error_message); 531 if (info.return_type.? != Table.BuildBestIndexError!void) @compileError(error_message);
523 } 532 }
524 533
525 if (!meta.trait.hasDecls(Table, .{"Cursor"})) { 534 if (!comptime hasDecls(Table, .{"Cursor"})) {
526 @compileError("the Table type must declare a Cursor type"); 535 @compileError("the Table type must declare a Cursor type");
527 } 536 }
528} 537}