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 a reference to the contained value, or `None` if this entry
145 /// holds a non-value expression.
146 pub fn as_value(&self) -> Option<&Value> {
147 match *self {
148 Entry::Expr(Expr::Value(value)) | Entry::Value(value) => Some(value),
149 _ => None,
150 }
151 }
152
153 /// Returns a reference to the contained value, panicking if this
154 /// entry does not hold a value.
155 ///
156 /// # Panics
157 ///
158 /// Panics if the entry is not a value.
159 #[track_caller]
160 pub fn as_value_unwrap(&self) -> &Value {
161 self.as_value()
162 .unwrap_or_else(|| panic!("expected Entry with value; actual={self:#?}"))
163 }
164
165 /// Extracts an owned [`Value`] from this entry, evaluating constant
166 /// expressions if needed.
167 ///
168 /// # Panics
169 ///
170 /// Panics if the entry contains a non-constant expression.
171 pub fn to_value(&self) -> Value {
172 match *self {
173 Entry::Expr(Expr::Value(value)) | Entry::Value(value) => value.clone(),
174 Entry::Expr(expr) => expr.eval_const().unwrap_or_else(|err| {
175 panic!("not const expression; entry={self:#?}; error={err:#?}")
176 }),
177 }
178 }
179}
180
181impl<'a> From<&'a Expr> for Entry<'a> {
182 fn from(value: &'a Expr) -> Self {
183 Entry::Expr(value)
184 }
185}
186
187impl<'a> From<&'a Value> for Entry<'a> {
188 fn from(value: &'a Value) -> Self {
189 Entry::Value(value)
190 }
191}
192
193impl<'a> From<Entry<'a>> for Expr {
194 fn from(value: Entry<'a>) -> Self {
195 match value {
196 Entry::Expr(expr) => expr.clone(),
197 Entry::Value(value) => value.clone().into(),
198 }
199 }
200}