const std = @import("std"); const _comptime = @import("utils/comptime.zig"); const _registry = @import("registry.zig"); const _dispatchers = @import("dispatchers.zig"); /// Compile-time model primary key definition structure. pub const ModelPrimaryKey = union(enum) { const Self = @This(); /// Simple single-column primary key. single: []const u8, /// Composite (multi-columns) primary key. composite: []const []const u8, //TODO Actually implement composite primary keys. /// Get string representation of the model primary key. pub fn toString(self: Self) []const u8 { return switch (self) { .single => |key| key, .composite => |keys| _comptime.join(", ", keys), }; } }; /// Compile-time model definition structure. pub const Model: type = struct { const Self = @This(); /// Get the structure type of the current model. pub fn structure(self: Self) type { return ModelStructure(self); } /// Initialize a new model structure with all values as NULL. pub fn initStruct(self: Self) self.structure() { // Initialize a model instance. var instance: self.structure() = undefined; inline for (@typeInfo(@TypeOf(instance)).Struct.fields) |field| { // Initialize all fields to null. @field(instance, field.name) = null; } return instance; } /// Table of the model. table: []const u8, /// Primary key of the model. primaryKey: ModelPrimaryKey, /// Model fields and relationships definition. definition: type, /// Get identifier of the model. pub fn getIdentifier(self: *const Self) []const u8 { return self.table ++ "(" ++ self.primaryKey.toString() ++ ")"; } }; /// Get the type of a model structure from its definition. pub fn ModelStructure(model: Model) type { // Initialize all fields of the structure. var structFields: [std.meta.declarations(model.definition).len]std.builtin.Type.StructField = undefined; // For each field in the definition, create its corresponding field in the structure. inline for (std.meta.declarations(model.definition), &structFields) |definitionDelc, *structField| { var fieldType: type = undefined; if (@hasField(@TypeOf(@field(model.definition, definitionDelc.name)), "type")) { // It's a simple type, getting the corresponding field type to create the structure field. fieldType = @Type(std.builtin.Type{ .Optional = .{ .child = @field(model.definition, definitionDelc.name).type } }); } else if (@hasField(@TypeOf(@field(model.definition, definitionDelc.name)), "related")) { // It's a relationship type, getting the corresponding field type. //const modelType = @field(registry.models, @field(model.definition, definitionDelc.name).related).structure(registry); fieldType = @Type(std.builtin.Type{ .Optional = .{ .child = *const anyopaque } //TODO Using anyopaque instead of modelType as a workaround of infinite recursion. }); } else { // Invalid declaration format. @compileError("invalid declaration \"" ++ definitionDelc.name ++ "\": cannot find its type."); } structField.* = std.builtin.Type.StructField{ .name = definitionDelc.name, .type = fieldType, .default_value = null, .is_comptime = false, .alignment = @alignOf(fieldType), }; } // Return the built structure. return @Type(std.builtin.Type{ .Struct = .{ .layout = std.builtin.Type.ContainerLayout.auto, .fields = &structFields, .decls = &[_]std.builtin.Type.Declaration{}, .is_tuple = false, }, }); }