toasty_core/stmt/
expr_reference.rs

1use crate::{
2    schema::{app::FieldId, db::ColumnId},
3    stmt::Expr,
4};
5
6/// A reference to a model, field, or column.
7///
8/// References use scope-based nesting to support subqueries. A nesting level of
9/// `0` refers to the current query scope, while higher levels reference higher
10/// scope queries.
11///
12/// # Examples
13///
14/// ```text
15/// ref(field: 0, nesting: 0)  // field 0 in current query
16/// ref(field: 2, nesting: 1)  // field 2 in parent query
17/// ref(column: 0, table: 1)   // column 0 in table 1
18/// ```
19#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)]
20pub enum ExprReference {
21    /// A reference to a column in a database-level statement.
22    ///
23    /// ExprReference::Column represents resolved column references after lowering from the
24    /// application schema to the database schema. It uses a scope-based approach
25    /// similar to ExprReference::Field, referencing a specific column within a target
26    /// at a given nesting level.
27    Column(ExprColumn),
28
29    /// Reference a specific field in a query's relation.
30    ///
31    /// For Query/Delete statements, the relation is the Source.
32    /// For Insert/Update statements, the relation is the target.
33    Field {
34        /// Query scope nesting level: 0 = current query, 1+ = higher scope queries
35        nesting: usize,
36        /// Index of the field within the relation
37        index: usize,
38    },
39
40    /// Reference a model at a specific nesting level.
41    ///
42    /// This is roughly referencing the full record instead of a specific field.
43    Model {
44        /// Query scope nesting level: 0 = current query, 1+ = higher scope queries.
45        nesting: usize,
46    },
47}
48
49/// A reference to a database column.
50///
51/// Used after lowering from the application schema to the database schema.
52#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)]
53pub struct ExprColumn {
54    /// Query scope nesting level: `0` = current query, `1`+ = higher scope queries.
55    pub nesting: usize,
56
57    /// Index into the table references vector for this column's source relation.
58    ///
59    /// For statements with multiple tables (SELECT with JOINs), this indexes into
60    /// the `SourceTable::tables` field to identify which specific table contains
61    /// this column. For single-target statements (INSERT, UPDATE), this is
62    /// typically 0 since these operations target only one relation at a time.
63    pub table: usize,
64
65    /// The index of the column in the table
66    pub column: usize,
67}
68
69impl Expr {
70    /// Returns `true` if this expression is a reference (field, column, or model).
71    pub fn is_expr_reference(&self) -> bool {
72        matches!(self, Expr::Reference(..))
73    }
74
75    /// Creates an expression that references a field in the current query.
76    ///
77    /// This creates an `ExprReference::Field` with `nesting = 0`, meaning it
78    /// references a field in the current query scope rather than an outer query.
79    ///
80    /// # Arguments
81    ///
82    /// * `field` - A field identifier that can be converted into a `FieldId`
83    ///
84    /// # Returns
85    ///
86    /// An `Expr::Reference` containing an `ExprReference::Field` that points to
87    /// the specified field in the current query's relation.
88    pub fn ref_self_field(field: impl Into<FieldId>) -> Self {
89        ExprReference::field(field).into()
90    }
91
92    /// Create a reference to a field at a specified nesting level
93    pub fn ref_field(nesting: usize, field: impl Into<FieldId>) -> Self {
94        ExprReference::Field {
95            nesting,
96            index: field.into().index,
97        }
98        .into()
99    }
100
101    /// Create a reference to a field one level up
102    pub fn ref_parent_field(field: impl Into<FieldId>) -> Self {
103        ExprReference::Field {
104            nesting: 1,
105            index: field.into().index,
106        }
107        .into()
108    }
109
110    /// Returns `true` if this is a field reference.
111    pub fn is_field(&self) -> bool {
112        matches!(self, Self::Reference(ExprReference::Field { .. }))
113    }
114
115    /// Creates a model reference to the parent (nesting level 1).
116    pub fn ref_parent_model() -> Self {
117        Self::ref_ancestor_model(1)
118    }
119
120    /// Create a model reference to the specified nesting level
121    pub fn ref_ancestor_model(nesting: usize) -> Self {
122        ExprReference::Model { nesting }.into()
123    }
124
125    /// Creates a column reference expression.
126    pub fn column(column: impl Into<ExprReference>) -> Self {
127        column.into().into()
128    }
129
130    /// Returns `true` if this expression is a column reference.
131    pub fn is_column(&self) -> bool {
132        matches!(self, Self::Reference(ExprReference::Column(..)))
133    }
134
135    /// Returns a reference to the inner [`ExprReference`] if this is a
136    /// reference expression, or `None` otherwise.
137    pub fn as_expr_reference(&self) -> Option<&ExprReference> {
138        match self {
139            Expr::Reference(expr_reference) => Some(expr_reference),
140            _ => None,
141        }
142    }
143
144    /// Returns a reference to the inner [`ExprReference`].
145    ///
146    /// # Panics
147    ///
148    /// Panics if `self` is not `Expr::Reference`.
149    #[track_caller]
150    pub fn as_expr_reference_unwrap(&self) -> &ExprReference {
151        self.as_expr_reference()
152            .unwrap_or_else(|| panic!("expected ExprReference; actual={self:#?}"))
153    }
154
155    /// Returns a reference to the inner [`ExprColumn`] if this is a column
156    /// reference, or `None` otherwise.
157    pub fn as_expr_column(&self) -> Option<&ExprColumn> {
158        match self {
159            Expr::Reference(ExprReference::Column(expr_column)) => Some(expr_column),
160            _ => None,
161        }
162    }
163
164    /// Returns a reference to the inner [`ExprColumn`].
165    ///
166    /// # Panics
167    ///
168    /// Panics if `self` is not `Expr::Reference(ExprReference::Column(_))`.
169    #[track_caller]
170    pub fn as_expr_column_unwrap(&self) -> &ExprColumn {
171        self.as_expr_column()
172            .unwrap_or_else(|| panic!("expected ExprColumn; actual={self:#?}"))
173    }
174}
175
176impl ExprReference {
177    /// Creates a field reference in the current scope (nesting = 0).
178    pub fn field(field: impl Into<FieldId>) -> Self {
179        ExprReference::Field {
180            nesting: 0,
181            index: field.into().index,
182        }
183    }
184
185    /// Returns `true` if this is a field reference.
186    pub fn is_field(&self) -> bool {
187        matches!(self, ExprReference::Field { .. })
188    }
189
190    /// Returns `true` if this is a model reference.
191    pub fn is_model(&self) -> bool {
192        matches!(self, ExprReference::Model { .. })
193    }
194
195    /// Creates a column reference in the current scope (nesting = 0).
196    pub fn column(table: usize, column: usize) -> Self {
197        ExprReference::Column(ExprColumn {
198            nesting: 0,
199            table,
200            column,
201        })
202    }
203
204    /// Returns `true` if this is a column reference.
205    pub fn is_column(&self) -> bool {
206        matches!(self, ExprReference::Column(..))
207    }
208
209    /// Returns a reference to the inner [`ExprColumn`] if this is a column
210    /// reference, or `None` otherwise.
211    pub fn as_expr_column(&self) -> Option<&ExprColumn> {
212        match self {
213            ExprReference::Column(expr_column) => Some(expr_column),
214            _ => None,
215        }
216    }
217
218    /// Returns a reference to the inner [`ExprColumn`].
219    ///
220    /// # Panics
221    ///
222    /// Panics if `self` is not `ExprReference::Column`.
223    #[track_caller]
224    pub fn as_expr_column_unwrap(&self) -> &ExprColumn {
225        self.as_expr_column()
226            .unwrap_or_else(|| panic!("expected ExprColumn; actual={self:#?}"))
227    }
228
229    /// Returns a mutable reference to the inner [`ExprColumn`] if this is a
230    /// column reference, or `None` otherwise.
231    pub fn as_expr_column_mut(&mut self) -> Option<&mut ExprColumn> {
232        match self {
233            ExprReference::Column(expr_column) => Some(expr_column),
234            _ => None,
235        }
236    }
237
238    /// Returns a mutable reference to the inner [`ExprColumn`].
239    ///
240    /// # Panics
241    ///
242    /// Panics if `self` is not `ExprReference::Column`.
243    #[track_caller]
244    pub fn as_expr_column_mut_unwrap(&mut self) -> &mut ExprColumn {
245        match self {
246            ExprReference::Column(expr_column) => expr_column,
247            _ => panic!("expected ExprColumn; actual={self:#?}"),
248        }
249    }
250}
251
252impl From<ExprColumn> for ExprReference {
253    fn from(value: ExprColumn) -> Self {
254        ExprReference::Column(value)
255    }
256}
257
258impl From<ExprReference> for Expr {
259    fn from(value: ExprReference) -> Self {
260        Expr::Reference(value)
261    }
262}
263
264impl From<ExprColumn> for Expr {
265    fn from(value: ExprColumn) -> Self {
266        Expr::Reference(ExprReference::Column(value))
267    }
268}
269
270impl From<&ExprReference> for Expr {
271    fn from(value: &ExprReference) -> Self {
272        Expr::Reference(*value)
273    }
274}
275
276impl From<&ExprColumn> for Expr {
277    fn from(value: &ExprColumn) -> Self {
278        Expr::Reference(ExprReference::Column(*value))
279    }
280}
281
282impl From<ColumnId> for ExprReference {
283    fn from(value: ColumnId) -> Self {
284        ExprReference::Column(value.into())
285    }
286}
287
288impl From<ColumnId> for ExprColumn {
289    fn from(value: ColumnId) -> Self {
290        ExprColumn {
291            nesting: 0,
292            table: 0,
293            column: value.index,
294        }
295    }
296}