use super::FieldType;
#[derive(Debug, Clone, Copy)]
pub struct FieldSchema {
pub name: &'static str,
pub column: &'static str,
pub ty: FieldType,
pub nullable: bool,
pub primary_key: bool,
pub relation: Option<Relation>,
pub max_length: Option<u32>,
pub min: Option<i64>,
pub max: Option<i64>,
pub default: Option<&'static str>,
pub auto: bool,
}
#[derive(Debug, Clone, Copy)]
pub enum Relation {
Fk { to: &'static str, on: &'static str },
O2O { to: &'static str, on: &'static str },
M2M {
to: &'static str,
through: &'static str,
src: &'static str,
dst: &'static str,
},
}
#[derive(Debug, Clone, Copy)]
pub struct ModelSchema {
pub name: &'static str,
pub table: &'static str,
pub fields: &'static [FieldSchema],
pub display: Option<&'static str>,
}
impl ModelSchema {
#[must_use]
pub fn field(&self, name: &str) -> Option<&'static FieldSchema> {
self.fields.iter().find(|f| f.name == name)
}
#[must_use]
pub fn field_by_column(&self, column: &str) -> Option<&'static FieldSchema> {
self.fields.iter().find(|f| f.column == column)
}
#[must_use]
pub fn primary_key(&self) -> Option<&'static FieldSchema> {
self.fields.iter().find(|f| f.primary_key)
}
pub fn scalar_fields(&self) -> impl Iterator<Item = &'static FieldSchema> {
self.fields
.iter()
.filter(|f| !matches!(f.relation, Some(Relation::M2M { .. })))
}
#[must_use]
pub fn display_field(&self) -> Option<&'static FieldSchema> {
if let Some(name) = self.display {
return self.field(name);
}
self.primary_key()
}
pub fn searchable_fields(&self) -> impl Iterator<Item = &'static FieldSchema> {
self.fields.iter().filter(|f| {
matches!(f.ty, FieldType::String) && f.max_length.is_some() && f.relation.is_none()
})
}
}
pub trait Model: Sized + Send + Sync + 'static {
const SCHEMA: &'static ModelSchema;
}
#[doc(hidden)]
pub struct ModelEntry {
pub schema: &'static ModelSchema,
}
inventory::collect!(ModelEntry);