toasty_core/schema/app/
field.rs

1mod primitive;
2pub use primitive::{FieldPrimitive, SerializeFormat};
3
4use super::{
5    AutoStrategy, BelongsTo, Constraint, Embedded, HasMany, HasOne, Model, ModelId, Schema,
6    VariantId,
7};
8use crate::{driver, stmt, Result};
9use std::fmt;
10
11#[derive(Debug, Clone)]
12pub struct Field {
13    /// Uniquely identifies the field within the containing model.
14    pub id: FieldId,
15
16    /// The field name
17    pub name: FieldName,
18
19    /// Primitive, relation, composite, ...
20    pub ty: FieldTy,
21
22    /// True if the field can be nullable (`None` in Rust).
23    pub nullable: bool,
24
25    /// True if the field is part of the primary key
26    pub primary_key: bool,
27
28    /// Specified if and how Toasty should automatically populate this field for new values
29    pub auto: Option<AutoStrategy>,
30
31    /// Any additional field constraints
32    pub constraints: Vec<Constraint>,
33
34    /// If this field belongs to an enum variant, identifies that variant.
35    /// `None` for fields on root models and embedded structs.
36    pub variant: Option<VariantId>,
37}
38
39#[derive(Copy, Clone, PartialEq, Eq, Hash)]
40#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
41pub struct FieldId {
42    pub model: ModelId,
43    pub index: usize,
44}
45
46#[derive(Debug, Clone)]
47pub struct FieldName {
48    pub app_name: String,
49    pub storage_name: Option<String>,
50}
51
52impl FieldName {
53    pub fn storage_name(&self) -> &str {
54        self.storage_name.as_ref().unwrap_or(&self.app_name)
55    }
56}
57
58#[derive(Clone)]
59pub enum FieldTy {
60    Primitive(FieldPrimitive),
61    Embedded(Embedded),
62    BelongsTo(BelongsTo),
63    HasMany(HasMany),
64    HasOne(HasOne),
65}
66
67impl Field {
68    /// Gets the id.
69    pub fn id(&self) -> FieldId {
70        self.id
71    }
72
73    /// Gets the name.
74    pub fn name(&self) -> &FieldName {
75        &self.name
76    }
77
78    /// Gets the type.
79    pub fn ty(&self) -> &FieldTy {
80        &self.ty
81    }
82
83    /// Gets whether the field is nullable.
84    pub fn nullable(&self) -> bool {
85        self.nullable
86    }
87
88    /// Gets the primary key.
89    pub fn primary_key(&self) -> bool {
90        self.primary_key
91    }
92
93    /// Gets the [`Auto`].
94    pub fn auto(&self) -> Option<&AutoStrategy> {
95        self.auto.as_ref()
96    }
97
98    pub fn is_auto_increment(&self) -> bool {
99        self.auto().map(|auto| auto.is_increment()).unwrap_or(false)
100    }
101
102    pub fn is_relation(&self) -> bool {
103        self.ty.is_relation()
104    }
105
106    /// Returns a fully qualified name for the field.
107    pub fn full_name(&self, schema: &Schema) -> String {
108        let model = schema.model(self.id.model);
109        format!(
110            "{}::{}",
111            model.name().upper_camel_case(),
112            self.name.app_name
113        )
114    }
115
116    /// If the field is a relation, return the relation's target ModelId.
117    pub fn relation_target_id(&self) -> Option<ModelId> {
118        match &self.ty {
119            FieldTy::BelongsTo(belongs_to) => Some(belongs_to.target),
120            FieldTy::HasMany(has_many) => Some(has_many.target),
121            _ => None,
122        }
123    }
124
125    /// If the field is a relation, return the target of the relation.
126    pub fn relation_target<'a>(&self, schema: &'a Schema) -> Option<&'a Model> {
127        self.relation_target_id().map(|id| schema.model(id))
128    }
129
130    /// The type the field **evaluates** too. This is the "expression type".
131    pub fn expr_ty(&self) -> &stmt::Type {
132        match &self.ty {
133            FieldTy::Primitive(primitive) => &primitive.ty,
134            FieldTy::Embedded(embedded) => &embedded.expr_ty,
135            FieldTy::BelongsTo(belongs_to) => &belongs_to.expr_ty,
136            FieldTy::HasMany(has_many) => &has_many.expr_ty,
137            FieldTy::HasOne(has_one) => &has_one.expr_ty,
138        }
139    }
140
141    pub fn pair(&self) -> Option<FieldId> {
142        match &self.ty {
143            FieldTy::Primitive(_) => None,
144            FieldTy::Embedded(_) => None,
145            FieldTy::BelongsTo(belongs_to) => belongs_to.pair,
146            FieldTy::HasMany(has_many) => Some(has_many.pair),
147            FieldTy::HasOne(has_one) => Some(has_one.pair),
148        }
149    }
150
151    pub(crate) fn verify(&self, db: &driver::Capability) -> Result<()> {
152        if let FieldTy::Primitive(primitive) = &self.ty {
153            if let Some(storage_ty) = &primitive.storage_ty {
154                storage_ty.verify(db)?;
155            }
156        }
157
158        Ok(())
159    }
160}
161
162impl FieldTy {
163    pub fn is_primitive(&self) -> bool {
164        matches!(self, Self::Primitive(..))
165    }
166
167    pub fn as_primitive(&self) -> Option<&FieldPrimitive> {
168        match self {
169            Self::Primitive(primitive) => Some(primitive),
170            _ => None,
171        }
172    }
173
174    #[track_caller]
175    pub fn expect_primitive(&self) -> &FieldPrimitive {
176        match self {
177            Self::Primitive(simple) => simple,
178            _ => panic!("expected simple field, but was {self:?}"),
179        }
180    }
181
182    #[track_caller]
183    pub fn expect_primitive_mut(&mut self) -> &mut FieldPrimitive {
184        match self {
185            Self::Primitive(simple) => simple,
186            _ => panic!("expected simple field, but was {self:?}"),
187        }
188    }
189
190    pub fn is_embedded(&self) -> bool {
191        matches!(self, Self::Embedded(..))
192    }
193
194    pub fn as_embedded(&self) -> Option<&Embedded> {
195        match self {
196            Self::Embedded(embedded) => Some(embedded),
197            _ => None,
198        }
199    }
200
201    #[track_caller]
202    pub fn expect_embedded(&self) -> &Embedded {
203        match self {
204            Self::Embedded(embedded) => embedded,
205            _ => panic!("expected embedded field, but was {self:?}"),
206        }
207    }
208
209    #[track_caller]
210    pub fn expect_embedded_mut(&mut self) -> &mut Embedded {
211        match self {
212            Self::Embedded(embedded) => embedded,
213            _ => panic!("expected embedded field, but was {self:?}"),
214        }
215    }
216
217    pub fn is_relation(&self) -> bool {
218        matches!(
219            self,
220            Self::BelongsTo(..) | Self::HasMany(..) | Self::HasOne(..)
221        )
222    }
223
224    pub fn is_has_n(&self) -> bool {
225        matches!(self, Self::HasMany(..) | Self::HasOne(..))
226    }
227
228    pub fn is_has_many(&self) -> bool {
229        matches!(self, Self::HasMany(..))
230    }
231
232    pub fn as_has_many(&self) -> Option<&HasMany> {
233        match self {
234            Self::HasMany(has_many) => Some(has_many),
235            _ => None,
236        }
237    }
238
239    #[track_caller]
240    pub fn expect_has_many(&self) -> &HasMany {
241        match self {
242            Self::HasMany(has_many) => has_many,
243            _ => panic!("expected field to be `HasMany`, but was {self:?}"),
244        }
245    }
246
247    #[track_caller]
248    pub fn expect_has_many_mut(&mut self) -> &mut HasMany {
249        match self {
250            Self::HasMany(has_many) => has_many,
251            _ => panic!("expected field to be `HasMany`, but was {self:?}"),
252        }
253    }
254
255    pub fn as_has_one(&self) -> Option<&HasOne> {
256        match self {
257            Self::HasOne(has_one) => Some(has_one),
258            _ => None,
259        }
260    }
261
262    pub fn is_has_one(&self) -> bool {
263        matches!(self, Self::HasOne(..))
264    }
265
266    #[track_caller]
267    pub fn expect_has_one(&self) -> &HasOne {
268        match self {
269            Self::HasOne(has_one) => has_one,
270            _ => panic!("expected field to be `HasOne`, but it was {self:?}"),
271        }
272    }
273
274    #[track_caller]
275    pub fn expect_has_one_mut(&mut self) -> &mut HasOne {
276        match self {
277            Self::HasOne(has_one) => has_one,
278            _ => panic!("expected field to be `HasOne`, but it was {self:?}"),
279        }
280    }
281
282    pub fn is_belongs_to(&self) -> bool {
283        matches!(self, Self::BelongsTo(..))
284    }
285
286    pub fn as_belongs_to(&self) -> Option<&BelongsTo> {
287        match self {
288            Self::BelongsTo(belongs_to) => Some(belongs_to),
289            _ => None,
290        }
291    }
292
293    #[track_caller]
294    pub fn expect_belongs_to(&self) -> &BelongsTo {
295        match self {
296            Self::BelongsTo(belongs_to) => belongs_to,
297            _ => panic!("expected field to be `BelongsTo`, but was {self:?}"),
298        }
299    }
300
301    #[track_caller]
302    pub fn expect_belongs_to_mut(&mut self) -> &mut BelongsTo {
303        match self {
304            Self::BelongsTo(belongs_to) => belongs_to,
305            _ => panic!("expected field to be `BelongsTo`, but was {self:?}"),
306        }
307    }
308}
309
310impl fmt::Debug for FieldTy {
311    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
312        match self {
313            Self::Primitive(ty) => ty.fmt(fmt),
314            Self::Embedded(ty) => ty.fmt(fmt),
315            Self::BelongsTo(ty) => ty.fmt(fmt),
316            Self::HasMany(ty) => ty.fmt(fmt),
317            Self::HasOne(ty) => ty.fmt(fmt),
318        }
319    }
320}
321
322impl FieldId {
323    pub(crate) fn placeholder() -> Self {
324        Self {
325            model: ModelId::placeholder(),
326            index: usize::MAX,
327        }
328    }
329}
330
331impl From<&Self> for FieldId {
332    fn from(val: &Self) -> Self {
333        *val
334    }
335}
336
337impl From<&Field> for FieldId {
338    fn from(val: &Field) -> Self {
339        val.id
340    }
341}
342
343impl From<FieldId> for usize {
344    fn from(val: FieldId) -> Self {
345        val.index
346    }
347}
348
349impl fmt::Debug for FieldId {
350    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
351        write!(fmt, "FieldId({}/{})", self.model.0, self.index)
352    }
353}