1use std::fmt;
6
7use crate::functions::{Aggregate, Function};
8use crate::value::Value;
9
10#[derive(Clone, Debug, PartialEq)]
20#[non_exhaustive]
21pub enum Criteria {
22 Value(Value),
24 Property(Vec<String>),
26 Parameter(String),
28 Unary(UnaryOp, Box<Criteria>),
30 Binary(BinaryOp, Box<Criteria>, Box<Criteria>),
32 Group(GroupOp, Vec<Criteria>),
34 In(Box<Criteria>, Vec<Criteria>),
36 Between(Box<Criteria>, Box<Criteria>, Box<Criteria>),
38 IsNull(Box<Criteria>),
40 Function(Function, Vec<Criteria>),
42 Custom(String, Vec<Criteria>),
44 Aggregate(Box<AggregateCall>),
46}
47
48#[derive(Clone, Debug, PartialEq)]
50pub struct AggregateCall {
51 pub collection: Option<Vec<String>>,
53 pub condition: Option<Criteria>,
55 pub function: Aggregate,
57 pub args: Vec<Criteria>,
59}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq)]
63#[non_exhaustive]
64pub enum UnaryOp {
65 Not,
67 Minus,
69 BitNot,
71}
72
73#[derive(Clone, Copy, Debug, PartialEq, Eq)]
75#[non_exhaustive]
76#[allow(missing_docs, reason = "the operators speak for themselves")]
77pub enum BinaryOp {
78 Add,
79 Sub,
80 Mul,
81 Div,
82 Rem,
83 Eq,
84 Ne,
85 Lt,
86 Le,
87 Gt,
88 Ge,
89 Like,
91 BitAnd,
92 BitOr,
93 BitXor,
94}
95
96#[derive(Clone, Copy, Debug, PartialEq, Eq)]
98#[non_exhaustive]
99pub enum GroupOp {
100 And,
102 Or,
104}
105
106impl BinaryOp {
107 fn symbol(self) -> &'static str {
108 match self {
109 Self::Add => "+",
110 Self::Sub => "-",
111 Self::Mul => "*",
112 Self::Div => "/",
113 Self::Rem => "%",
114 Self::Eq => "=",
115 Self::Ne => "<>",
116 Self::Lt => "<",
117 Self::Le => "<=",
118 Self::Gt => ">",
119 Self::Ge => ">=",
120 Self::Like => "Like",
121 Self::BitAnd => "&",
122 Self::BitOr => "|",
123 Self::BitXor => "^",
124 }
125 }
126
127 fn precedence(self) -> u8 {
128 match self {
129 Self::BitOr => 4,
130 Self::BitXor => 5,
131 Self::BitAnd => 6,
132 Self::Eq | Self::Ne | Self::Lt | Self::Le | Self::Gt | Self::Ge | Self::Like => 7,
133 Self::Add | Self::Sub => 8,
134 Self::Mul | Self::Div | Self::Rem => 9,
135 }
136 }
137}
138
139impl std::ops::Not for Criteria {
140 type Output = Self;
141
142 fn not(self) -> Self {
144 Self::Unary(UnaryOp::Not, Box::new(self))
145 }
146}
147
148impl Criteria {
149 pub fn parse(source: &str) -> Result<Self, crate::ParseError> {
151 crate::parse::parse(source)
152 }
153
154 pub fn assignments(source: &str) -> Result<Vec<(String, Self)>, crate::ParseError> {
165 crate::parse::parse_assignments(source)
166 }
167
168 pub fn property(path: &str) -> Self {
170 Self::Property(path.split('.').map(str::to_owned).collect())
171 }
172
173 pub fn value(value: impl Into<Value>) -> Self {
175 Self::Value(value.into())
176 }
177
178 pub fn equals(self, other: Self) -> Self {
180 Self::Binary(BinaryOp::Eq, Box::new(self), Box::new(other))
181 }
182
183 pub fn and(self, other: Self) -> Self {
185 Self::group(GroupOp::And, self, other)
186 }
187
188 pub fn or(self, other: Self) -> Self {
190 Self::group(GroupOp::Or, self, other)
191 }
192
193 fn group(op: GroupOp, a: Self, b: Self) -> Self {
194 let mut items = Vec::new();
195 for c in [a, b] {
196 match c {
197 Self::Group(o, inner) if o == op => items.extend(inner),
198 other => items.push(other),
199 }
200 }
201 Self::Group(op, items)
202 }
203
204 pub fn walk(&self, f: &mut dyn FnMut(&Criteria)) {
206 f(self);
207 match self {
208 Self::Value(_) | Self::Property(_) | Self::Parameter(_) => {}
209 Self::Unary(_, a) | Self::IsNull(a) => a.walk(f),
210 Self::Binary(_, a, b) => {
211 a.walk(f);
212 b.walk(f);
213 }
214 Self::Group(_, items) | Self::Function(_, items) | Self::Custom(_, items) => {
215 items.iter().for_each(|c| c.walk(f));
216 }
217 Self::In(a, items) => {
218 a.walk(f);
219 items.iter().for_each(|c| c.walk(f));
220 }
221 Self::Between(a, b, c) => {
222 a.walk(f);
223 b.walk(f);
224 c.walk(f);
225 }
226 Self::Aggregate(call) => {
227 if let Some(c) = &call.condition {
228 c.walk(f);
229 }
230 call.args.iter().for_each(|c| c.walk(f));
231 }
232 }
233 }
234
235 pub fn properties(&self) -> Vec<String> {
237 let mut out = Vec::new();
238 self.walk(&mut |c| {
239 if let Self::Property(p) = c {
240 let path = p.join(".");
241 if !out.contains(&path) {
242 out.push(path);
243 }
244 }
245 });
246 out
247 }
248
249 pub fn parameters(&self) -> Vec<String> {
251 let mut out = Vec::new();
252 self.walk(&mut |c| {
253 if let Self::Parameter(p) = c
254 && !out.contains(p)
255 {
256 out.push(p.clone());
257 }
258 });
259 out
260 }
261
262 pub fn any(&self, test: &dyn Fn(&Criteria) -> bool) -> bool {
264 let mut found = false;
265 self.walk(&mut |c| found |= test(c));
266 found
267 }
268
269 fn precedence(&self) -> u8 {
271 match self {
272 Self::Group(GroupOp::Or, _) => 1,
273 Self::Group(GroupOp::And, _) => 2,
274 Self::Unary(UnaryOp::Not, _) => 3,
275 Self::Binary(op, ..) => op.precedence(),
276 Self::In(..) | Self::Between(..) | Self::IsNull(_) => 7,
277 Self::Unary(..) => 10,
278 _ => 11,
279 }
280 }
281
282 fn write_child(&self, f: &mut fmt::Formatter<'_>, min: u8) -> fmt::Result {
283 if self.precedence() < min { write!(f, "({self})") } else { write!(f, "{self}") }
284 }
285}
286
287fn write_list(f: &mut fmt::Formatter<'_>, items: &[Criteria]) -> fmt::Result {
288 for (i, c) in items.iter().enumerate() {
289 if i > 0 {
290 f.write_str(", ")?;
291 }
292 write!(f, "{c}")?;
293 }
294 Ok(())
295}
296
297impl fmt::Display for Criteria {
298 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
299 match self {
300 Self::Value(v) => match v {
301 Value::Null => f.write_str("Null"),
302 Value::Bool(true) => f.write_str("True"),
303 Value::Bool(false) => f.write_str("False"),
304 Value::Number(n) if *n < 0.0 => write!(f, "({})", crate::value::format_plain(*n)),
305 Value::Number(n) => f.write_str(&crate::value::format_plain(*n)),
306 Value::Text(s) => write!(f, "'{}'", s.replace('\'', "''")),
307 Value::Date(d) => write!(f, "#{d}#"),
308 },
309 Self::Property(p) => write!(f, "[{}]", p.join(".")),
310 Self::Parameter(p) => write!(f, "?{p}"),
311 Self::Unary(UnaryOp::Not, a) => {
312 f.write_str("Not ")?;
313 a.write_child(f, 3)
314 }
315 Self::Unary(UnaryOp::Minus, a) => {
316 f.write_str("-")?;
317 a.write_child(f, 10)
318 }
319 Self::Unary(UnaryOp::BitNot, a) => {
320 f.write_str("~")?;
321 a.write_child(f, 10)
322 }
323 Self::Binary(op, a, b) => {
324 let p = op.precedence();
325 a.write_child(f, p)?;
326 write!(f, " {} ", op.symbol())?;
327 b.write_child(f, p + 1)
329 }
330 Self::Group(op, items) => {
331 let (word, p) = if *op == GroupOp::And { (" And ", 3) } else { (" Or ", 2) };
332 for (i, c) in items.iter().enumerate() {
333 if i > 0 {
334 f.write_str(word)?;
335 }
336 c.write_child(f, p)?;
337 }
338 Ok(())
339 }
340 Self::In(a, items) => {
341 a.write_child(f, 8)?;
342 f.write_str(" In (")?;
343 write_list(f, items)?;
344 f.write_str(")")
345 }
346 Self::Between(a, lo, hi) => {
347 a.write_child(f, 8)?;
348 write!(f, " Between({lo}, {hi})")
349 }
350 Self::IsNull(a) => {
351 a.write_child(f, 8)?;
352 f.write_str(" Is Null")
353 }
354 Self::Function(func, args) => {
355 write!(f, "{}(", func.name())?;
356 write_list(f, args)?;
357 f.write_str(")")
358 }
359 Self::Custom(name, args) => {
360 write!(f, "{name}(")?;
361 write_list(f, args)?;
362 f.write_str(")")
363 }
364 Self::Aggregate(call) => {
365 if let Some(path) = &call.collection {
366 write!(f, "[{}]", path.join("."))?;
367 match &call.condition {
368 Some(c) => write!(f, "[{c}].")?,
369 None => f.write_str("[].")?,
370 }
371 }
372 write!(f, "{}(", call.function.name())?;
373 write_list(f, &call.args)?;
374 f.write_str(")")
375 }
376 }
377 }
378}