pgmql/lib/model.zig

102 lines
3.4 KiB
Zig
Raw Permalink Normal View History

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,
},
});
}