use super::{Field, FieldId, FieldPrimitive, Index, Name, PrimaryKey};
use crate::{Result, driver, stmt};
use std::fmt;
#[derive(Debug, Clone)]
pub enum Model {
Root(ModelRoot),
EmbeddedStruct(EmbeddedStruct),
EmbeddedEnum(EmbeddedEnum),
}
#[derive(Debug, Clone, Default)]
pub struct ModelSet {
models: Vec<Model>,
}
impl ModelSet {
pub fn new() -> Self {
Self::default()
}
pub fn len(&self) -> usize {
self.models.len()
}
pub fn is_empty(&self) -> bool {
self.models.is_empty()
}
pub fn add(&mut self, model: Model) {
self.models.push(model);
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = &Model> {
self.models.iter()
}
}
impl<'a> IntoIterator for &'a ModelSet {
type Item = &'a Model;
type IntoIter = std::slice::Iter<'a, Model>;
fn into_iter(self) -> Self::IntoIter {
self.models.iter()
}
}
impl IntoIterator for ModelSet {
type Item = Model;
type IntoIter = std::vec::IntoIter<Model>;
fn into_iter(self) -> Self::IntoIter {
self.models.into_iter()
}
}
#[derive(Debug, Clone)]
pub struct ModelRoot {
pub id: ModelId,
pub name: Name,
pub fields: Vec<Field>,
pub primary_key: PrimaryKey,
pub table_name: Option<String>,
pub indices: Vec<Index>,
}
impl ModelRoot {
pub fn find_by_id(&self, mut input: impl stmt::Input) -> stmt::Query {
let filter = match &self.primary_key.fields[..] {
[pk_field] => stmt::Expr::eq(
stmt::Expr::ref_self_field(pk_field),
input
.resolve_arg(&0.into(), &stmt::Projection::identity())
.unwrap(),
),
pk_fields => stmt::Expr::and_from_vec(
pk_fields
.iter()
.enumerate()
.map(|(i, pk_field)| {
stmt::Expr::eq(
stmt::Expr::ref_self_field(pk_field),
input
.resolve_arg(&i.into(), &stmt::Projection::identity())
.unwrap(),
)
})
.collect(),
),
};
stmt::Query::new_select(self.id, filter)
}
pub fn primary_key_fields(&self) -> impl ExactSizeIterator<Item = &'_ Field> {
self.primary_key
.fields
.iter()
.map(|pk_field| &self.fields[pk_field.index])
}
pub fn field_by_name(&self, name: &str) -> Option<&Field> {
self.fields.iter().find(|field| field.name.app_name == name)
}
pub(crate) fn verify(&self, db: &driver::Capability) -> Result<()> {
for field in &self.fields {
field.verify(db)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct EmbeddedStruct {
pub id: ModelId,
pub name: Name,
pub fields: Vec<Field>,
pub indices: Vec<Index>,
}
impl EmbeddedStruct {
pub(crate) fn verify(&self, db: &driver::Capability) -> Result<()> {
for field in &self.fields {
field.verify(db)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct EmbeddedEnum {
pub id: ModelId,
pub name: Name,
pub discriminant: FieldPrimitive,
pub variants: Vec<EnumVariant>,
pub fields: Vec<Field>,
pub indices: Vec<Index>,
}
#[derive(Debug, Clone)]
pub struct EnumVariant {
pub name: Name,
pub discriminant: stmt::Value,
}
impl EmbeddedEnum {
pub fn has_data_variants(&self) -> bool {
!self.fields.is_empty()
}
pub fn variant_fields(&self, variant_index: usize) -> impl Iterator<Item = &Field> {
let variant_id = VariantId {
model: self.id,
index: variant_index,
};
self.fields
.iter()
.filter(move |f| f.variant == Some(variant_id))
}
pub(crate) fn verify(&self, db: &driver::Capability) -> Result<()> {
for field in &self.fields {
field.verify(db)?;
}
Ok(())
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ModelId(pub usize);
impl Model {
pub fn id(&self) -> ModelId {
match self {
Model::Root(root) => root.id,
Model::EmbeddedStruct(embedded) => embedded.id,
Model::EmbeddedEnum(e) => e.id,
}
}
pub fn name(&self) -> &Name {
match self {
Model::Root(root) => &root.name,
Model::EmbeddedStruct(embedded) => &embedded.name,
Model::EmbeddedEnum(e) => &e.name,
}
}
pub fn is_root(&self) -> bool {
matches!(self, Model::Root(_))
}
pub fn is_embedded(&self) -> bool {
matches!(self, Model::EmbeddedStruct(_) | Model::EmbeddedEnum(_))
}
pub fn can_be_relation_target(&self) -> bool {
self.is_root()
}
pub fn as_root(&self) -> Option<&ModelRoot> {
match self {
Model::Root(root) => Some(root),
_ => None,
}
}
pub fn as_root_unwrap(&self) -> &ModelRoot {
match self {
Model::Root(root) => root,
Model::EmbeddedStruct(_) => panic!("expected root model, found embedded struct"),
Model::EmbeddedEnum(_) => panic!("expected root model, found embedded enum"),
}
}
pub fn as_root_mut_unwrap(&mut self) -> &mut ModelRoot {
match self {
Model::Root(root) => root,
Model::EmbeddedStruct(_) => panic!("expected root model, found embedded struct"),
Model::EmbeddedEnum(_) => panic!("expected root model, found embedded enum"),
}
}
pub fn as_embedded_struct_unwrap(&self) -> &EmbeddedStruct {
match self {
Model::EmbeddedStruct(embedded) => embedded,
Model::Root(_) => panic!("expected embedded struct, found root model"),
Model::EmbeddedEnum(_) => panic!("expected embedded struct, found embedded enum"),
}
}
pub fn as_embedded_enum_unwrap(&self) -> &EmbeddedEnum {
match self {
Model::EmbeddedEnum(e) => e,
Model::Root(_) => panic!("expected embedded enum, found root model"),
Model::EmbeddedStruct(_) => panic!("expected embedded enum, found embedded struct"),
}
}
pub(crate) fn verify(&self, db: &driver::Capability) -> Result<()> {
match self {
Model::Root(root) => root.verify(db),
Model::EmbeddedStruct(embedded) => embedded.verify(db),
Model::EmbeddedEnum(e) => e.verify(db),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct VariantId {
pub model: ModelId,
pub index: usize,
}
impl fmt::Debug for VariantId {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "VariantId({}/{})", self.model.0, self.index)
}
}
impl ModelId {
pub const fn field(self, index: usize) -> FieldId {
FieldId { model: self, index }
}
pub const fn variant(self, index: usize) -> VariantId {
VariantId { model: self, index }
}
pub(crate) const fn placeholder() -> Self {
Self(usize::MAX)
}
}
impl From<&Self> for ModelId {
fn from(src: &Self) -> Self {
*src
}
}
impl From<&mut Self> for ModelId {
fn from(src: &mut Self) -> Self {
*src
}
}
impl From<&Model> for ModelId {
fn from(value: &Model) -> Self {
value.id()
}
}
impl From<&ModelRoot> for ModelId {
fn from(value: &ModelRoot) -> Self {
value.id
}
}
impl fmt::Debug for ModelId {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "ModelId({})", self.0)
}
}