toasty_core/stmt/
insert_target.rs

1use super::{Expr, InsertTable, Query};
2use crate::schema::app::ModelId;
3
4/// The target of an [`Insert`](super::Insert) statement.
5///
6/// Specifies where new records should be inserted. At the model level this is
7/// typically a model ID or a scoped query. After lowering, it becomes a table
8/// with column mappings.
9///
10/// # Examples
11///
12/// ```ignore
13/// use toasty_core::stmt::InsertTarget;
14/// use toasty_core::schema::app::ModelId;
15///
16/// let target = InsertTarget::Model(ModelId(0));
17/// assert!(target.is_model());
18/// assert!(!target.is_table());
19/// ```
20#[derive(Debug, Clone, PartialEq)]
21pub enum InsertTarget {
22    /// Insert into a scoped query. The inserted value should satisfy the
23    /// query's filter, which may set default field values or validate
24    /// existing ones.
25    Scope(Box<Query>),
26
27    /// Insert a model by its ID.
28    Model(ModelId),
29
30    /// Insert into a database table (lowered form).
31    Table(InsertTable),
32}
33
34impl InsertTarget {
35    /// Returns `true` if this target is a `Model` variant.
36    pub fn is_model(&self) -> bool {
37        matches!(self, InsertTarget::Model(..))
38    }
39
40    /// Returns the model ID for this target.
41    ///
42    /// For `Scope` targets, extracts the model ID from the inner query's
43    /// select source.
44    ///
45    /// # Panics
46    ///
47    /// Panics if this is a `Table` variant.
48    #[track_caller]
49    pub fn model_id_unwrap(&self) -> ModelId {
50        match self {
51            Self::Scope(query) => query.body.as_select_unwrap().source.model_id_unwrap(),
52            Self::Model(model_id) => *model_id,
53            _ => panic!("expected InsertTarget::Model; actual={self:#?}"),
54        }
55    }
56
57    /// Returns `true` if this target is a `Table` variant.
58    pub fn is_table(&self) -> bool {
59        matches!(self, InsertTarget::Table(..))
60    }
61
62    /// Returns a reference to the inner [`InsertTable`] if this is a `Table`
63    /// variant.
64    pub fn as_table(&self) -> Option<&InsertTable> {
65        match self {
66            Self::Table(table) => Some(table),
67            _ => None,
68        }
69    }
70
71    /// Returns a reference to the inner [`InsertTable`].
72    ///
73    /// # Panics
74    ///
75    /// Panics if this is not a `Table` variant.
76    #[track_caller]
77    pub fn as_table_unwrap(&self) -> &InsertTable {
78        self.as_table()
79            .unwrap_or_else(|| panic!("expected InsertTarget::Table; actual={self:#?}"))
80    }
81
82    /// Adds a constraint expression to this target.
83    ///
84    /// For `Scope` targets, the expression is added as a filter. For `Model`
85    /// targets, the target is upgraded to a `Scope` wrapping a select query
86    /// with the constraint as a filter.
87    pub fn add_constraint(&mut self, expr: impl Into<Expr>) {
88        let expr = expr.into();
89        match self {
90            Self::Scope(query) => query.add_filter(expr),
91            Self::Model(model_id) => {
92                *self = Self::Scope(Box::new(Query::new_select(*model_id, expr)));
93            }
94            _ => todo!("{self:#?}"),
95        }
96    }
97}
98
99impl From<Query> for InsertTarget {
100    fn from(value: Query) -> Self {
101        Self::Scope(Box::new(value))
102    }
103}