Skip to main content

qail_core/ast/
values.rs

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