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}