diff options
| author | 2023-11-23 21:43:30 +0100 | |
|---|---|---|
| committer | 2023-11-23 21:43:30 +0100 | |
| commit | 123eb09609c09f1187a1d29bd5dbed9e0962ec54 (patch) | |
| tree | 191c0ac15659665390a6e958a98208dc8d08053a | |
| parent | Merge pull request #143 from edyu/master (diff) | |
| parent | use @hasField (diff) | |
| download | zig-sqlite-123eb09609c09f1187a1d29bd5dbed9e0962ec54.tar.gz zig-sqlite-123eb09609c09f1187a1d29bd5dbed9e0962ec54.tar.xz zig-sqlite-123eb09609c09f1187a1d29bd5dbed9e0962ec54.zip | |
Merge branch 'fix-latest-zig'
| -rw-r--r-- | helpers.zig | 16 | ||||
| -rw-r--r-- | sqlite.zig | 64 | ||||
| -rw-r--r-- | tools/preprocess_files.zig | 4 | ||||
| -rw-r--r-- | vtab.zig | 49 |
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`. | ||
| 91 | pub 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 | } | ||
| @@ -21,6 +21,7 @@ const getDetailedErrorFromResultCode = errors.getDetailedErrorFromResultCode; | |||
| 21 | 21 | ||
| 22 | const getTestDb = @import("test.zig").getTestDb; | 22 | const getTestDb = @import("test.zig").getTestDb; |
| 23 | pub const vtab = @import("vtab.zig"); | 23 | pub const vtab = @import("vtab.zig"); |
| 24 | |||
| 24 | const helpers = @import("helpers.zig"); | 25 | const helpers = @import("helpers.zig"); |
| 25 | 26 | ||
| 26 | test { | 27 | test { |
| @@ -29,6 +30,43 @@ test { | |||
| 29 | 30 | ||
| 30 | const logger = std.log.scoped(.sqlite); | 31 | const logger = std.log.scoped(.sqlite); |
| 31 | 32 | ||
| 33 | // Returns true if the passed type is a struct. | ||
| 34 | fn 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. | ||
| 42 | fn 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. |
| 33 | pub const Text = struct { data: []const u8 }; | 71 | pub 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 | ||
| @@ -16,6 +16,15 @@ const helpers = @import("helpers.zig"); | |||
| 16 | 16 | ||
| 17 | const logger = std.log.scoped(.vtab); | 17 | const logger = std.log.scoped(.vtab); |
| 18 | 18 | ||
| 19 | fn 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 { | |||
| 459 | fn validateTableType(comptime Table: type) void { | 468 | fn 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 | } |