Skip to main content

qail_core/ast/
values.rs

1use crate::ast::Qail;
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5/// Time interval unit for duration expressions
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7pub enum IntervalUnit {
8    /// Seconds.
9    Second,
10    /// Minutes.
11    Minute,
12    /// Hours.
13    Hour,
14    /// Days.
15    Day,
16    /// Weeks.
17    Week,
18    /// Months.
19    Month,
20    /// Years.
21    Year,
22}
23
24impl std::fmt::Display for IntervalUnit {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        match self {
27            IntervalUnit::Second => write!(f, "seconds"),
28            IntervalUnit::Minute => write!(f, "minutes"),
29            IntervalUnit::Hour => write!(f, "hours"),
30            IntervalUnit::Day => write!(f, "days"),
31            IntervalUnit::Week => write!(f, "weeks"),
32            IntervalUnit::Month => write!(f, "months"),
33            IntervalUnit::Year => write!(f, "years"),
34        }
35    }
36}
37
38/// A value in a condition.
39#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
40pub enum Value {
41    /// SQL NULL.
42    Null,
43    /// Boolean.
44    Bool(bool),
45    /// 64-bit integer.
46    Int(i64),
47    /// 64-bit float.
48    Float(f64),
49    /// Text string.
50    String(String),
51    /// Positional parameter ($n).
52    Param(usize),
53    /// Named parameter reference (:name, :id, etc.)
54    NamedParam(String),
55    /// SQL function call.
56    Function(String),
57    /// Array of values.
58    Array(Vec<Value>),
59    /// Subquery.
60    Subquery(Box<Qail>),
61    /// Column reference.
62    Column(String),
63    /// UUID.
64    Uuid(Uuid),
65    /// NULL-typed UUID.
66    NullUuid,
67    /// Time interval (e.g., 24 hours, 7 days)
68    Interval {
69        /// Numeric amount.
70        amount: i64,
71        /// Unit of time.
72        unit: IntervalUnit,
73    },
74    /// Timestamp literal.
75    Timestamp(String),
76    /// Binary data (bytea)
77    Bytes(Vec<u8>),
78    /// AST Expression (for complex expression comparisons like col > NOW() - INTERVAL)
79    Expr(Box<crate::ast::Expr>),
80    /// Vector embedding for similarity search (Qdrant)
81    Vector(Vec<f32>),
82    /// JSON data.
83    Json(String),
84}
85
86impl std::fmt::Display for Value {
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        match self {
89            Value::Null => write!(f, "NULL"),
90            Value::Bool(b) => write!(f, "{}", b),
91            Value::Int(n) => write!(f, "{}", n),
92            Value::Float(n) => write!(f, "{}", n),
93            Value::String(s) => write!(f, "'{}'", s.replace('\'', "''")),
94            Value::Param(n) => write!(f, "${}", n),
95            Value::NamedParam(name) => write!(f, ":{}", name),
96            Value::Function(s) => write!(f, "{}", s),
97            Value::Array(arr) => {
98                write!(f, "(")?;
99                for (i, v) in arr.iter().enumerate() {
100                    if i > 0 {
101                        write!(f, ", ")?;
102                    }
103                    write!(f, "{}", v)?;
104                }
105                write!(f, ")")
106            }
107            Value::Subquery(_) => write!(f, "(SUBQUERY)"),
108            Value::Column(s) => write!(f, "{}", s),
109            Value::Uuid(u) => write!(f, "'{}'", u),
110            Value::NullUuid => write!(f, "NULL"),
111            Value::Interval { amount, unit } => write!(f, "INTERVAL '{} {}'", amount, unit),
112            Value::Timestamp(ts) => write!(f, "'{}'", ts),
113            Value::Bytes(bytes) => {
114                write!(f, "'\\x")?;
115                for byte in bytes {
116                    write!(f, "{:02x}", byte)?;
117                }
118                write!(f, "'")
119            }
120            Value::Expr(expr) => write!(f, "{}", expr),
121            Value::Vector(v) => {
122                write!(f, "[")?;
123                for (i, val) in v.iter().enumerate() {
124                    if i > 0 {
125                        write!(f, ", ")?;
126                    }
127                    write!(f, "{}", val)?;
128                }
129                write!(f, "]")
130            }
131            Value::Json(json) => write!(f, "'{}'::jsonb", json.replace('\'', "''")),
132        }
133    }
134}
135
136impl From<bool> for Value {
137    fn from(b: bool) -> Self {
138        Value::Bool(b)
139    }
140}
141
142impl From<i32> for Value {
143    fn from(n: i32) -> Self {
144        Value::Int(n as i64)
145    }
146}
147
148impl From<i64> for Value {
149    fn from(n: i64) -> Self {
150        Value::Int(n)
151    }
152}
153
154impl From<f64> for Value {
155    fn from(n: f64) -> Self {
156        Value::Float(n)
157    }
158}
159
160impl From<&str> for Value {
161    fn from(s: &str) -> Self {
162        Value::String(s.to_string())
163    }
164}
165
166impl From<String> for Value {
167    fn from(s: String) -> Self {
168        Value::String(s)
169    }
170}
171
172impl From<Uuid> for Value {
173    fn from(u: Uuid) -> Self {
174        Value::Uuid(u)
175    }
176}
177
178impl From<Option<Uuid>> for Value {
179    fn from(opt: Option<Uuid>) -> Self {
180        match opt {
181            Some(u) => Value::Uuid(u),
182            None => Value::NullUuid,
183        }
184    }
185}
186
187impl From<Option<String>> for Value {
188    fn from(opt: Option<String>) -> Self {
189        match opt {
190            Some(s) => Value::String(s),
191            None => Value::Null,
192        }
193    }
194}
195
196impl<'a> From<Option<&'a str>> for Value {
197    fn from(opt: Option<&'a str>) -> Self {
198        match opt {
199            Some(s) => Value::String(s.to_string()),
200            None => Value::Null,
201        }
202    }
203}
204
205impl From<Option<i64>> for Value {
206    fn from(opt: Option<i64>) -> Self {
207        match opt {
208            Some(n) => Value::Int(n),
209            None => Value::Null,
210        }
211    }
212}
213
214impl From<Option<i32>> for Value {
215    fn from(opt: Option<i32>) -> Self {
216        match opt {
217            Some(n) => Value::Int(n as i64),
218            None => Value::Null,
219        }
220    }
221}
222
223impl From<Option<bool>> for Value {
224    fn from(opt: Option<bool>) -> Self {
225        match opt {
226            Some(b) => Value::Bool(b),
227            None => Value::Null,
228        }
229    }
230}
231
232/// Convert an Expr into a Value for use in correlated subquery filters.
233///
234/// # Example
235/// ```ignore
236/// use qail_core::ast::builders::col;
237///
238/// // WHERE session_id = parent_table.id
239/// .eq("session_id", col("parent_table.id"))
240/// ```
241impl From<crate::ast::Expr> for Value {
242    fn from(expr: crate::ast::Expr) -> Self {
243        Value::Expr(Box::new(expr))
244    }
245}