toasty_core/stmt/entry.rs
1use crate::Result;
2
3use super::{Expr, 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!(matches!(entry, Entry::Value(_)));
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 as a constant expression.
32 ///
33 /// For `Entry::Expr`, attempts to evaluate the expression without any input context.
34 /// This only succeeds if the expression is constant (contains no references or arguments).
35 /// For `Entry::Value`, returns a clone of the value directly.
36 ///
37 /// # Errors
38 ///
39 /// Returns an error if the entry contains an expression that cannot be evaluated
40 /// as a constant (e.g., references to columns or arguments).
41 ///
42 /// # Examples
43 ///
44 /// ```
45 /// # use toasty_core::stmt::{Entry, Value};
46 /// let value = Value::from("hello");
47 /// let entry = Entry::from(&value);
48 ///
49 /// let result = entry.eval_const().unwrap();
50 /// assert_eq!(result, Value::from("hello"));
51 /// ```
52 pub fn eval_const(&self) -> Result<Value> {
53 match self {
54 Entry::Expr(expr) => expr.eval_const(),
55 Entry::Value(value) => Ok((*value).clone()),
56 }
57 }
58
59 /// Returns `true` if the entry is a constant expression.
60 ///
61 /// An entry is considered constant if it does not reference any external data:
62 /// - `Entry::Value` is always constant
63 /// - `Entry::Expr` is constant if the expression itself is constant
64 /// (see [`Expr::is_const`] for details)
65 ///
66 /// Constant entries can be evaluated without any input context.
67 ///
68 /// # Examples
69 ///
70 /// ```
71 /// # use toasty_core::stmt::{Entry, Value, Expr};
72 /// // Values are always constant
73 /// let value = Value::from(42);
74 /// let entry = Entry::from(&value);
75 /// assert!(entry.is_const());
76 ///
77 /// // Constant expressions
78 /// let expr = Expr::from(Value::from("hello"));
79 /// let entry = Entry::from(&expr);
80 /// assert!(entry.is_const());
81 /// ```
82 pub fn is_const(&self) -> bool {
83 match self {
84 Entry::Value(_) => true,
85 Entry::Expr(expr) => expr.is_const(),
86 }
87 }
88
89 /// Converts this entry to an owned [`Expr`] by cloning the contained
90 /// expression or wrapping the value.
91 pub fn to_expr(&self) -> Expr {
92 match *self {
93 Entry::Expr(expr) => expr.clone(),
94 Entry::Value(value) => value.clone().into(),
95 }
96 }
97
98 /// Returns `true` if this entry is `Expr::Default`.
99 pub fn is_expr_default(&self) -> bool {
100 matches!(self, Entry::Expr(Expr::Default))
101 }
102
103 /// Returns `true` if this entry holds a concrete value (either
104 /// `Entry::Value` or `Entry::Expr(Expr::Value(_))`).
105 pub fn is_value(&self) -> bool {
106 matches!(self, Entry::Value(_) | Entry::Expr(Expr::Value(_)))
107 }
108
109 /// Returns `true` if this entry holds a null value.
110 pub fn is_value_null(&self) -> bool {
111 matches!(
112 self,
113 Entry::Value(Value::Null) | Entry::Expr(Expr::Value(Value::Null))
114 )
115 }
116
117 /// Returns a reference to the contained value, or `None` if this entry
118 /// holds a non-value expression.
119 pub fn as_value(&self) -> Option<&Value> {
120 match *self {
121 Entry::Expr(Expr::Value(value)) | Entry::Value(value) => Some(value),
122 _ => None,
123 }
124 }
125
126 /// Returns a reference to the contained value, panicking if this
127 /// entry does not hold a value.
128 ///
129 /// # Panics
130 ///
131 /// Panics if the entry is not a value.
132 #[track_caller]
133 pub fn as_value_unwrap(&self) -> &Value {
134 self.as_value()
135 .unwrap_or_else(|| panic!("expected Entry with value; actual={self:#?}"))
136 }
137
138 /// Extracts an owned [`Value`] from this entry, evaluating constant
139 /// expressions if needed.
140 ///
141 /// # Panics
142 ///
143 /// Panics if the entry contains a non-constant expression.
144 pub fn to_value(&self) -> Value {
145 match *self {
146 Entry::Expr(Expr::Value(value)) | Entry::Value(value) => value.clone(),
147 Entry::Expr(expr) => expr.eval_const().unwrap_or_else(|err| {
148 panic!("not const expression; entry={self:#?}; error={err:#?}")
149 }),
150 }
151 }
152}
153
154impl<'a> From<&'a Expr> for Entry<'a> {
155 fn from(value: &'a Expr) -> Self {
156 Entry::Expr(value)
157 }
158}
159
160impl<'a> From<&'a Value> for Entry<'a> {
161 fn from(value: &'a Value) -> Self {
162 Entry::Value(value)
163 }
164}
165
166impl<'a> From<Entry<'a>> for Expr {
167 fn from(value: Entry<'a>) -> Self {
168 match value {
169 Entry::Expr(expr) => expr.clone(),
170 Entry::Value(value) => value.clone().into(),
171 }
172 }
173}