Skip to main content

toasty_core/stmt/
entry.rs

1use crate::Result;
2
3use super::{Expr, Input, Value};
4
5/// A borrowed reference to either an [`Expr`] or a [`Value`] within a
6/// composite structure.
7///
8/// `Entry` is returned by navigation methods (e.g., [`Value::entry`],
9/// [`Expr::entry`]) and provides a uniform way to inspect or evaluate the
10/// referenced data without cloning.
11///
12/// # Examples
13///
14/// ```
15/// use toasty_core::stmt::{Entry, Value};
16///
17/// let value = Value::from(42_i64);
18/// let entry = Entry::from(&value);
19/// assert!(entry.is_value());
20/// assert!(!entry.is_expr());
21/// ```
22#[derive(Debug)]
23pub enum Entry<'a> {
24    /// A reference to an expression.
25    Expr(&'a Expr),
26    /// A reference to a value.
27    Value(&'a Value),
28}
29
30impl Entry<'_> {
31    /// Evaluates the entry to a value using the provided input.
32    ///
33    /// For `Entry::Expr`, evaluates the expression with the given input context.
34    /// For `Entry::Value`, returns a clone of the value directly.
35    ///
36    /// # Examples
37    ///
38    /// ```
39    /// # use toasty_core::stmt::{Entry, Value, ConstInput};
40    /// let value = Value::from(42);
41    /// let entry = Entry::from(&value);
42    ///
43    /// let result = entry.eval(ConstInput::new()).unwrap();
44    /// assert_eq!(result, Value::from(42));
45    /// ```
46    pub fn eval(&self, input: impl Input) -> Result<Value> {
47        match self {
48            Entry::Expr(expr) => expr.eval(input),
49            Entry::Value(value) => Ok((*value).clone()),
50        }
51    }
52
53    /// Evaluates the entry as a constant expression.
54    ///
55    /// For `Entry::Expr`, attempts to evaluate the expression without any input context.
56    /// This only succeeds if the expression is constant (contains no references or arguments).
57    /// For `Entry::Value`, returns a clone of the value directly.
58    ///
59    /// # Errors
60    ///
61    /// Returns an error if the entry contains an expression that cannot be evaluated
62    /// as a constant (e.g., references to columns or arguments).
63    ///
64    /// # Examples
65    ///
66    /// ```
67    /// # use toasty_core::stmt::{Entry, Value};
68    /// let value = Value::from("hello");
69    /// let entry = Entry::from(&value);
70    ///
71    /// let result = entry.eval_const().unwrap();
72    /// assert_eq!(result, Value::from("hello"));
73    /// ```
74    pub fn eval_const(&self) -> Result<Value> {
75        match self {
76            Entry::Expr(expr) => expr.eval_const(),
77            Entry::Value(value) => Ok((*value).clone()),
78        }
79    }
80
81    /// Returns `true` if the entry is a constant expression.
82    ///
83    /// An entry is considered constant if it does not reference any external data:
84    /// - `Entry::Value` is always constant
85    /// - `Entry::Expr` is constant if the expression itself is constant
86    ///   (see [`Expr::is_const`] for details)
87    ///
88    /// Constant entries can be evaluated without any input context.
89    ///
90    /// # Examples
91    ///
92    /// ```
93    /// # use toasty_core::stmt::{Entry, Value, Expr};
94    /// // Values are always constant
95    /// let value = Value::from(42);
96    /// let entry = Entry::from(&value);
97    /// assert!(entry.is_const());
98    ///
99    /// // Constant expressions
100    /// let expr = Expr::from(Value::from("hello"));
101    /// let entry = Entry::from(&expr);
102    /// assert!(entry.is_const());
103    /// ```
104    pub fn is_const(&self) -> bool {
105        match self {
106            Entry::Value(_) => true,
107            Entry::Expr(expr) => expr.is_const(),
108        }
109    }
110
111    /// Returns `true` if this entry contains an expression.
112    pub fn is_expr(&self) -> bool {
113        matches!(self, Entry::Expr(_))
114    }
115
116    /// Converts this entry to an owned [`Expr`] by cloning the contained
117    /// expression or wrapping the value.
118    pub fn to_expr(&self) -> Expr {
119        match *self {
120            Entry::Expr(expr) => expr.clone(),
121            Entry::Value(value) => value.clone().into(),
122        }
123    }
124
125    /// Returns `true` if this entry is `Expr::Default`.
126    pub fn is_expr_default(&self) -> bool {
127        matches!(self, Entry::Expr(Expr::Default))
128    }
129
130    /// Returns `true` if this entry holds a concrete value (either
131    /// `Entry::Value` or `Entry::Expr(Expr::Value(_))`).
132    pub fn is_value(&self) -> bool {
133        matches!(self, Entry::Value(_) | Entry::Expr(Expr::Value(_)))
134    }
135
136    /// Returns `true` if this entry holds a null value.
137    pub fn is_value_null(&self) -> bool {
138        matches!(
139            self,
140            Entry::Value(Value::Null) | Entry::Expr(Expr::Value(Value::Null))
141        )
142    }
143
144    /// Returns `true` if this entry holds a record, either as an
145    /// `Expr::Record`, an `Expr::Value(Value::Record)`, or a bare
146    /// `Value::Record`.
147    pub fn is_record(&self) -> bool {
148        match self {
149            Entry::Expr(Expr::Record(_)) => true,
150            Entry::Expr(Expr::Value(value)) => value.is_record(),
151            Entry::Value(value) => value.is_record(),
152            Entry::Expr(_) => false,
153        }
154    }
155
156    /// Returns a reference to the contained value, or `None` if this entry
157    /// holds a non-value expression.
158    pub fn as_value(&self) -> Option<&Value> {
159        match *self {
160            Entry::Expr(Expr::Value(value)) | Entry::Value(value) => Some(value),
161            _ => None,
162        }
163    }
164
165    /// Returns a reference to the contained value, panicking if this
166    /// entry does not hold a value.
167    ///
168    /// # Panics
169    ///
170    /// Panics if the entry is not a value.
171    #[track_caller]
172    pub fn as_value_unwrap(&self) -> &Value {
173        self.as_value()
174            .unwrap_or_else(|| panic!("expected Entry with value; actual={self:#?}"))
175    }
176
177    /// Extracts an owned [`Value`] from this entry, evaluating constant
178    /// expressions if needed.
179    ///
180    /// # Panics
181    ///
182    /// Panics if the entry contains a non-constant expression.
183    pub fn to_value(&self) -> Value {
184        match *self {
185            Entry::Expr(Expr::Value(value)) | Entry::Value(value) => value.clone(),
186            Entry::Expr(expr) => expr.eval_const().unwrap_or_else(|err| {
187                panic!("not const expression; entry={self:#?}; error={err:#?}")
188            }),
189        }
190    }
191}
192
193impl<'a> From<&'a Expr> for Entry<'a> {
194    fn from(value: &'a Expr) -> Self {
195        Entry::Expr(value)
196    }
197}
198
199impl<'a> From<&'a Value> for Entry<'a> {
200    fn from(value: &'a Value) -> Self {
201        Entry::Value(value)
202    }
203}
204
205impl<'a> From<Entry<'a>> for Expr {
206    fn from(value: Entry<'a>) -> Self {
207        match value {
208            Entry::Expr(expr) => expr.clone(),
209            Entry::Value(value) => value.clone().into(),
210        }
211    }
212}