tellaro_query_language/parser/
ast.rs1use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10#[serde(tag = "type", rename_all = "snake_case")]
11pub enum AstNode {
12 MatchAll,
14
15 Comparison(ComparisonNode),
17
18 LogicalOp(LogicalOpNode),
20
21 UnaryOp(UnaryOpNode),
23
24 CollectionOp(CollectionOpNode),
26
27 GeoExpr(GeoExprNode),
29
30 NslookupExpr(NslookupExprNode),
32
33 StatsExpr(StatsNode),
35
36 QueryWithStats(QueryWithStatsNode),
38}
39
40#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
42pub struct ComparisonNode {
43 pub field: String,
45
46 pub operator: String,
48
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub value: Option<Value>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub field_mutators: Option<Vec<Mutator>>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub value_mutators: Option<Vec<Mutator>>,
60
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub type_hint: Option<String>,
64}
65
66#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
68pub struct LogicalOpNode {
69 pub operator: String,
71
72 pub left: Box<AstNode>,
74
75 pub right: Box<AstNode>,
77}
78
79#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
81pub struct UnaryOpNode {
82 pub operator: String,
84
85 pub operand: Box<AstNode>,
87}
88
89#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
91pub struct CollectionOpNode {
92 pub operator: String,
94
95 pub field: String,
97
98 pub comparison_operator: String,
100
101 pub value: Value,
103
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub field_mutators: Option<Vec<Mutator>>,
107
108 #[serde(skip_serializing_if = "Option::is_none")]
110 pub type_hint: Option<String>,
111}
112
113#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
115pub struct GeoExprNode {
116 pub field: String,
118
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub field_mutators: Option<Vec<Mutator>>,
122
123 #[serde(skip_serializing_if = "Option::is_none")]
125 pub type_hint: Option<String>,
126
127 #[serde(skip_serializing_if = "Option::is_none")]
129 pub conditions: Option<Box<AstNode>>,
130
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub geo_params: Option<HashMap<String, Value>>,
134}
135
136#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
138pub struct NslookupExprNode {
139 pub field: String,
141
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub field_mutators: Option<Vec<Mutator>>,
145
146 #[serde(skip_serializing_if = "Option::is_none")]
148 pub type_hint: Option<String>,
149
150 #[serde(skip_serializing_if = "Option::is_none")]
152 pub conditions: Option<Box<AstNode>>,
153
154 #[serde(skip_serializing_if = "Option::is_none")]
156 pub nslookup_params: Option<HashMap<String, Value>>,
157}
158
159#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
161#[serde(untagged)]
162pub enum VizParamValue {
163 String(String),
165 Integer(i64),
167 Float(f64),
169 Boolean(bool),
171}
172
173#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
175pub struct StatsNode {
176 pub aggregations: Vec<Aggregation>,
178
179 #[serde(skip_serializing_if = "Vec::is_empty", default)]
181 pub group_by: Vec<GroupBy>,
182
183 #[serde(skip_serializing_if = "Option::is_none")]
185 pub viz_hint: Option<String>,
186
187 #[serde(skip_serializing_if = "Option::is_none")]
189 pub viz_params: Option<HashMap<String, VizParamValue>>,
190}
191
192#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
194pub struct QueryWithStatsNode {
195 pub filter: Box<AstNode>,
197
198 pub stats: StatsNode,
200}
201
202#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
204pub struct Aggregation {
205 pub function: String,
207
208 #[serde(skip_serializing_if = "Option::is_none")]
210 pub field: Option<String>,
211
212 #[serde(skip_serializing_if = "Option::is_none")]
214 pub alias: Option<String>,
215
216 #[serde(skip_serializing_if = "Option::is_none")]
218 pub modifier: Option<String>,
219
220 #[serde(skip_serializing_if = "Option::is_none")]
222 pub limit: Option<usize>,
223
224 #[serde(skip_serializing_if = "Option::is_none")]
226 pub percentile_values: Option<Vec<f64>>,
227
228 #[serde(skip_serializing_if = "Option::is_none")]
230 pub rank_values: Option<Vec<f64>>,
231
232 #[serde(skip_serializing_if = "Option::is_none")]
234 pub field_mutators: Option<Vec<Mutator>>,
235}
236
237#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
239pub struct GroupBy {
240 pub field: String,
242
243 #[serde(skip_serializing_if = "Option::is_none")]
245 pub bucket_size: Option<usize>,
246}
247
248#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
250pub struct Mutator {
251 pub name: String,
253
254 #[serde(skip_serializing_if = "Vec::is_empty", default)]
256 pub args: Vec<Value>,
257
258 #[serde(skip_serializing_if = "std::collections::HashMap::is_empty", default)]
260 pub named_args: std::collections::HashMap<String, Value>,
261}
262
263#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
265#[serde(untagged)]
266pub enum Value {
267 String(String),
269
270 Integer(i64),
272
273 Float(f64),
275
276 Boolean(bool),
278
279 List(Vec<Value>),
281
282 Null,
284}
285
286impl Value {
287 pub fn is_null(&self) -> bool {
289 matches!(self, Value::Null)
290 }
291
292 pub fn as_string(&self) -> Option<&str> {
294 match self {
295 Value::String(s) => Some(s),
296 _ => None,
297 }
298 }
299
300 pub fn as_integer(&self) -> Option<i64> {
302 match self {
303 Value::Integer(i) => Some(*i),
304 Value::Float(f) => Some(*f as i64),
305 _ => None,
306 }
307 }
308
309 pub fn as_float(&self) -> Option<f64> {
311 match self {
312 Value::Float(f) => Some(*f),
313 Value::Integer(i) => Some(*i as f64),
314 _ => None,
315 }
316 }
317
318 pub fn as_bool(&self) -> Option<bool> {
320 match self {
321 Value::Boolean(b) => Some(*b),
322 _ => None,
323 }
324 }
325
326 pub fn as_list(&self) -> Option<&Vec<Value>> {
328 match self {
329 Value::List(l) => Some(l),
330 _ => None,
331 }
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338
339 #[test]
340 fn test_value_conversions() {
341 let str_val = Value::String("test".to_string());
342 assert_eq!(str_val.as_string(), Some("test"));
343 assert!(str_val.as_integer().is_none());
344
345 let int_val = Value::Integer(42);
346 assert_eq!(int_val.as_integer(), Some(42));
347 assert_eq!(int_val.as_float(), Some(42.0));
348
349 let null_val = Value::Null;
350 assert!(null_val.is_null());
351 }
352
353 #[test]
354 fn test_serialization() {
355 let node = ComparisonNode {
356 field: "test".to_string(),
357 operator: "eq".to_string(),
358 value: Some(Value::String("value".to_string())),
359 field_mutators: None,
360 value_mutators: None,
361 type_hint: None,
362 };
363
364 let json = serde_json::to_string(&node).unwrap();
365 assert!(json.contains("\"field\":\"test\""));
366 }
367}