toasty_core/schema/app/
model.rs

1use super::{Field, FieldId, FieldPrimitive, Index, Name, PrimaryKey};
2use crate::{driver, stmt, Result};
3use std::fmt;
4
5#[derive(Debug, Clone)]
6pub enum Model {
7    /// Root model that maps to a database table and can be queried directly
8    Root(ModelRoot),
9    /// Embedded struct model that is flattened into its parent model's table
10    EmbeddedStruct(EmbeddedStruct),
11    /// Embedded enum model stored as a discriminant integer column
12    EmbeddedEnum(EmbeddedEnum),
13}
14
15#[derive(Debug, Clone)]
16pub struct ModelRoot {
17    /// Uniquely identifies the model within the schema
18    pub id: ModelId,
19
20    /// Name of the model
21    pub name: Name,
22
23    /// Fields contained by the model
24    pub fields: Vec<Field>,
25
26    /// The primary key for this model. Root models must have a primary key.
27    pub primary_key: PrimaryKey,
28
29    /// If the schema specifies a table to map the model to, this is set.
30    pub table_name: Option<String>,
31
32    /// Indices defined on this model.
33    pub indices: Vec<Index>,
34}
35
36impl ModelRoot {
37    pub fn find_by_id(&self, mut input: impl stmt::Input) -> stmt::Query {
38        let filter = match &self.primary_key.fields[..] {
39            [pk_field] => stmt::Expr::eq(
40                stmt::Expr::ref_self_field(pk_field),
41                input
42                    .resolve_arg(&0.into(), &stmt::Projection::identity())
43                    .unwrap(),
44            ),
45            pk_fields => stmt::Expr::and_from_vec(
46                pk_fields
47                    .iter()
48                    .enumerate()
49                    .map(|(i, pk_field)| {
50                        stmt::Expr::eq(
51                            stmt::Expr::ref_self_field(pk_field),
52                            input
53                                .resolve_arg(&i.into(), &stmt::Projection::identity())
54                                .unwrap(),
55                        )
56                    })
57                    .collect(),
58            ),
59        };
60
61        stmt::Query::new_select(self.id, filter)
62    }
63
64    /// Iterate over the fields used for the model's primary key.
65    pub fn primary_key_fields(&self) -> impl ExactSizeIterator<Item = &'_ Field> {
66        self.primary_key
67            .fields
68            .iter()
69            .map(|pk_field| &self.fields[pk_field.index])
70    }
71
72    pub fn field_by_name(&self, name: &str) -> Option<&Field> {
73        self.fields.iter().find(|field| field.name.app_name == name)
74    }
75
76    pub(crate) fn verify(&self, db: &driver::Capability) -> Result<()> {
77        for field in &self.fields {
78            field.verify(db)?;
79        }
80        Ok(())
81    }
82}
83
84#[derive(Debug, Clone)]
85pub struct EmbeddedStruct {
86    /// Uniquely identifies the model within the schema
87    pub id: ModelId,
88
89    /// Name of the model
90    pub name: Name,
91
92    /// Fields contained by the embedded struct
93    pub fields: Vec<Field>,
94
95    /// Indices defined on this embedded struct's fields.
96    ///
97    /// These reference fields within this embedded struct (not the parent model).
98    /// The schema builder propagates them to physical DB indexes on the parent
99    /// table's flattened columns.
100    pub indices: Vec<Index>,
101}
102
103impl EmbeddedStruct {
104    pub(crate) fn verify(&self, db: &driver::Capability) -> Result<()> {
105        for field in &self.fields {
106            field.verify(db)?;
107        }
108        Ok(())
109    }
110}
111
112#[derive(Debug, Clone)]
113pub struct EmbeddedEnum {
114    /// Uniquely identifies the model within the schema
115    pub id: ModelId,
116
117    /// Name of the model
118    pub name: Name,
119
120    /// The primitive type used for the discriminant column
121    pub discriminant: FieldPrimitive,
122
123    /// The enum's variants
124    pub variants: Vec<EnumVariant>,
125
126    /// All fields across all variants, with global indices. Each field's
127    /// `variant` field identifies which variant it belongs to.
128    pub fields: Vec<Field>,
129
130    /// Indices defined on this embedded enum's variant fields.
131    ///
132    /// These reference fields within this embedded enum (not the parent model).
133    /// The schema builder propagates them to physical DB indexes on the parent
134    /// table's flattened columns.
135    pub indices: Vec<Index>,
136}
137
138#[derive(Debug, Clone)]
139pub struct EnumVariant {
140    /// The Rust variant name
141    pub name: Name,
142
143    /// The discriminant value stored in the database column
144    pub discriminant: i64,
145}
146
147impl EmbeddedEnum {
148    /// Returns true if at least one variant carries data fields.
149    pub fn has_data_variants(&self) -> bool {
150        !self.fields.is_empty()
151    }
152
153    /// Returns fields belonging to a specific variant.
154    pub fn variant_fields(&self, variant_index: usize) -> impl Iterator<Item = &Field> {
155        let variant_id = VariantId {
156            model: self.id,
157            index: variant_index,
158        };
159        self.fields
160            .iter()
161            .filter(move |f| f.variant == Some(variant_id))
162    }
163
164    pub(crate) fn verify(&self, db: &driver::Capability) -> Result<()> {
165        for field in &self.fields {
166            field.verify(db)?;
167        }
168        Ok(())
169    }
170}
171
172#[derive(Copy, Clone, Eq, PartialEq, Hash)]
173#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
174pub struct ModelId(pub usize);
175
176impl Model {
177    pub fn id(&self) -> ModelId {
178        match self {
179            Model::Root(root) => root.id,
180            Model::EmbeddedStruct(embedded) => embedded.id,
181            Model::EmbeddedEnum(e) => e.id,
182        }
183    }
184
185    pub fn name(&self) -> &Name {
186        match self {
187            Model::Root(root) => &root.name,
188            Model::EmbeddedStruct(embedded) => &embedded.name,
189            Model::EmbeddedEnum(e) => &e.name,
190        }
191    }
192
193    /// Returns true if this is a root model (has a table and primary key)
194    pub fn is_root(&self) -> bool {
195        matches!(self, Model::Root(_))
196    }
197
198    /// Returns true if this is an embedded model (flattened into parent)
199    pub fn is_embedded(&self) -> bool {
200        matches!(self, Model::EmbeddedStruct(_) | Model::EmbeddedEnum(_))
201    }
202
203    /// Returns true if this model can be the target of a relation
204    pub fn can_be_relation_target(&self) -> bool {
205        self.is_root()
206    }
207
208    pub fn as_root(&self) -> Option<&ModelRoot> {
209        match self {
210            Model::Root(root) => Some(root),
211            _ => None,
212        }
213    }
214
215    /// Returns a reference to the root model data, panicking if this is not a root model.
216    pub fn expect_root(&self) -> &ModelRoot {
217        match self {
218            Model::Root(root) => root,
219            Model::EmbeddedStruct(_) => panic!("expected root model, found embedded struct"),
220            Model::EmbeddedEnum(_) => panic!("expected root model, found embedded enum"),
221        }
222    }
223
224    /// Returns a mutable reference to the root model data, panicking if this is not a root model.
225    pub fn expect_root_mut(&mut self) -> &mut ModelRoot {
226        match self {
227            Model::Root(root) => root,
228            Model::EmbeddedStruct(_) => panic!("expected root model, found embedded struct"),
229            Model::EmbeddedEnum(_) => panic!("expected root model, found embedded enum"),
230        }
231    }
232
233    /// Returns a reference to the embedded struct data, panicking if this is not an embedded struct.
234    pub fn expect_embedded_struct(&self) -> &EmbeddedStruct {
235        match self {
236            Model::EmbeddedStruct(embedded) => embedded,
237            Model::Root(_) => panic!("expected embedded struct, found root model"),
238            Model::EmbeddedEnum(_) => panic!("expected embedded struct, found embedded enum"),
239        }
240    }
241
242    /// Returns a reference to the embedded enum data, panicking if this is not an embedded enum.
243    pub fn expect_embedded_enum(&self) -> &EmbeddedEnum {
244        match self {
245            Model::EmbeddedEnum(e) => e,
246            Model::Root(_) => panic!("expected embedded enum, found root model"),
247            Model::EmbeddedStruct(_) => panic!("expected embedded enum, found embedded struct"),
248        }
249    }
250
251    pub(crate) fn verify(&self, db: &driver::Capability) -> Result<()> {
252        match self {
253            Model::Root(root) => root.verify(db),
254            Model::EmbeddedStruct(embedded) => embedded.verify(db),
255            Model::EmbeddedEnum(e) => e.verify(db),
256        }
257    }
258}
259
260/// Identifies a specific variant within an embedded enum model.
261#[derive(Copy, Clone, PartialEq, Eq, Hash)]
262pub struct VariantId {
263    /// The enum model this variant belongs to.
264    pub model: ModelId,
265    /// Index of the variant within `EmbeddedEnum::variants`.
266    pub index: usize,
267}
268
269impl fmt::Debug for VariantId {
270    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
271        write!(fmt, "VariantId({}/{})", self.model.0, self.index)
272    }
273}
274
275impl ModelId {
276    /// Create a `FieldId` representing the current model's field at index
277    /// `index`.
278    pub const fn field(self, index: usize) -> FieldId {
279        FieldId { model: self, index }
280    }
281
282    /// Create a `VariantId` representing the current model's variant at
283    /// `index`.
284    pub const fn variant(self, index: usize) -> VariantId {
285        VariantId { model: self, index }
286    }
287
288    pub(crate) const fn placeholder() -> Self {
289        Self(usize::MAX)
290    }
291}
292
293impl From<&Self> for ModelId {
294    fn from(src: &Self) -> Self {
295        *src
296    }
297}
298
299impl From<&mut Self> for ModelId {
300    fn from(src: &mut Self) -> Self {
301        *src
302    }
303}
304
305impl From<&Model> for ModelId {
306    fn from(value: &Model) -> Self {
307        value.id()
308    }
309}
310
311impl From<&ModelRoot> for ModelId {
312    fn from(value: &ModelRoot) -> Self {
313        value.id
314    }
315}
316
317impl fmt::Debug for ModelId {
318    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
319        write!(fmt, "ModelId({})", self.0)
320    }
321}