Skip to main content

rest_sql/
ast.rs

1use std::ops::{BitAnd, BitOr};
2
3/// Raw AST — unvalidated, produced directly by the parser.
4#[derive(Debug, Clone, PartialEq)]
5pub enum Ast {
6    And(Vec<Ast>),
7    Or(Vec<Ast>),
8    Constraint(Constraint),
9}
10
11impl Ast {
12    /// Combines `nodes` with AND. Panics if `nodes` is empty; unwraps if single element.
13    pub fn and(nodes: impl IntoIterator<Item = Ast>) -> Ast {
14        let v: Vec<_> = nodes.into_iter().collect();
15        assert!(!v.is_empty(), "Ast::and requires at least one node");
16        if v.len() == 1 {
17            v.into_iter().next().unwrap()
18        } else {
19            Ast::And(v)
20        }
21    }
22
23    /// Combines `nodes` with OR. Panics if `nodes` is empty; unwraps if single element.
24    pub fn or(nodes: impl IntoIterator<Item = Ast>) -> Ast {
25        let v: Vec<_> = nodes.into_iter().collect();
26        assert!(!v.is_empty(), "Ast::or requires at least one node");
27        if v.len() == 1 {
28            v.into_iter().next().unwrap()
29        } else {
30            Ast::Or(v)
31        }
32    }
33
34    /// Like `and`, but returns `None` for an empty iterator.
35    pub fn try_and(nodes: impl IntoIterator<Item = Ast>) -> Option<Ast> {
36        let v: Vec<_> = nodes.into_iter().collect();
37        if v.is_empty() {
38            None
39        } else if v.len() == 1 {
40            v.into_iter().next()
41        } else {
42            Some(Ast::And(v))
43        }
44    }
45
46    /// Like `try_or`, but returns `None` for an empty iterator.
47    pub fn try_or(nodes: impl IntoIterator<Item = Ast>) -> Option<Ast> {
48        let v: Vec<_> = nodes.into_iter().collect();
49        if v.is_empty() {
50            None
51        } else if v.len() == 1 {
52            v.into_iter().next()
53        } else {
54            Some(Ast::Or(v))
55        }
56    }
57
58    /// Like `try_and`, but accepts optional nodes — `None` entries are silently skipped.
59    ///
60    /// Useful when building a filter from a mix of optional query parameters:
61    ///
62    /// ```rust
63    /// use rest_sql::{Ast, filter::{eq, gte}};
64    ///
65    /// let ast = Ast::try_and_opts([
66    ///     Some(eq("genre", "Drama")),
67    ///     None,                        // absent query param — skipped
68    ///     Some(gte("year", 2000i64)),
69    /// ]);
70    /// // equivalent to: Some(eq("genre", "Drama") & gte("year", 2000))
71    /// ```
72    pub fn try_and_opts(nodes: impl IntoIterator<Item = Option<Ast>>) -> Option<Ast> {
73        Self::try_and(nodes.into_iter().flatten())
74    }
75
76    /// Like `try_or`, but accepts optional nodes — `None` entries are silently skipped.
77    pub fn try_or_opts(nodes: impl IntoIterator<Item = Option<Ast>>) -> Option<Ast> {
78        Self::try_or(nodes.into_iter().flatten())
79    }
80}
81
82/// `And & And` → flat `And`; `And & other` → appends to the `And` list; otherwise wraps in a new `And`.
83impl BitAnd for Ast {
84    type Output = Ast;
85    fn bitand(self, rhs: Ast) -> Ast {
86        match (self, rhs) {
87            (Ast::And(mut a), Ast::And(b)) => {
88                a.extend(b);
89                Ast::And(a)
90            }
91            (Ast::And(mut a), rhs) => {
92                a.push(rhs);
93                Ast::And(a)
94            }
95            (lhs, rhs) => Ast::And(vec![lhs, rhs]),
96        }
97    }
98}
99
100/// `Or | Or` → flat `Or`; `Or | other` → appends to the `Or` list; otherwise wraps in a new `Or`.
101impl BitOr for Ast {
102    type Output = Ast;
103    fn bitor(self, rhs: Ast) -> Ast {
104        match (self, rhs) {
105            (Ast::Or(mut a), Ast::Or(b)) => {
106                a.extend(b);
107                Ast::Or(a)
108            }
109            (Ast::Or(mut a), rhs) => {
110                a.push(rhs);
111                Ast::Or(a)
112            }
113            (lhs, rhs) => Ast::Or(vec![lhs, rhs]),
114        }
115    }
116}
117
118#[derive(Debug, Clone, PartialEq)]
119pub struct Constraint {
120    pub field: String,
121    pub operator: Operator,
122    pub value: Value,
123}
124
125#[derive(Debug, Clone, PartialEq)]
126pub enum Operator {
127    Eq,
128    Neq,
129    Lt,
130    Lte,
131    Gt,
132    Gte,
133    In,
134    Out,
135    Like,
136    Ilike,
137    Between,
138    Null,
139    NotNull,
140}
141
142#[derive(Debug, Clone, PartialEq)]
143pub enum Value {
144    Null,
145    Bool(bool),
146    Int(i64),
147    Float(f64),
148    String(String),
149    Date(String),
150    DateTime(String),
151    List(Vec<Value>),
152}
153
154impl From<bool> for Value {
155    fn from(v: bool) -> Self {
156        Value::Bool(v)
157    }
158}
159
160impl From<i32> for Value {
161    fn from(v: i32) -> Self {
162        Value::Int(v as i64)
163    }
164}
165
166impl From<i64> for Value {
167    fn from(v: i64) -> Self {
168        Value::Int(v)
169    }
170}
171
172impl From<f32> for Value {
173    fn from(v: f32) -> Self {
174        Value::Float(v as f64)
175    }
176}
177
178impl From<f64> for Value {
179    fn from(v: f64) -> Self {
180        Value::Float(v)
181    }
182}
183
184impl From<&str> for Value {
185    fn from(v: &str) -> Self {
186        Value::String(v.to_owned())
187    }
188}
189
190impl From<String> for Value {
191    fn from(v: String) -> Self {
192        Value::String(v)
193    }
194}