102 lines
3.4 KiB
Zig
102 lines
3.4 KiB
Zig
|
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,
|
||
|
},
|
||
|
});
|
||
|
}
|