1use crate::{geometry::spatial_op, temporal::temporal_op, Error, Geometry, Validator};
2use geo_types::{coord, Geometry as GGeom, Rect};
3use json_dotpath::DotPaths;
4use like::Like;
5use pg_escape::{quote_identifier, quote_literal};
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::{
9 collections::HashSet,
10 fmt::Debug,
11 ops::{Add, Deref},
12 str::FromStr,
13};
14use unaccent::unaccent;
15use wkt::TryFromWkt;
16
17pub const BOOLOPS: &[&str] = &["and", "or"];
19
20pub const EQOPS: &[&str] = &["=", "<>"];
22
23pub const CMPOPS: &[&str] = &[">", ">=", "<", "<="];
25
26pub const SPATIALOPS: &[&str] = &[
28 "s_equals",
29 "s_intersects",
30 "s_disjoint",
31 "s_touches",
32 "s_within",
33 "s_overlaps",
34 "s_crosses",
35 "s_contains",
36];
37
38pub const TEMPORALOPS: &[&str] = &[
40 "t_before",
41 "t_after",
42 "t_meets",
43 "t_metby",
44 "t_overlaps",
45 "t_overlappedby",
46 "t_starts",
47 "t_startedby",
48 "t_during",
49 "t_contains",
50 "t_finishes",
51 "t_finishedby",
52 "t_equals",
53 "t_disjoint",
54 "t_intersects",
55];
56
57pub const ARITHOPS: &[&str] = &["+", "-", "*", "/", "%", "^", "div"];
59
60pub const ARRAYOPS: &[&str] = &["a_equals", "a_contains", "a_containedby", "a_overlaps"];
62
63#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, PartialOrd)]
80#[serde(untagged)]
81#[allow(missing_docs)]
82pub enum Expr {
83 Operation { op: String, args: Vec<Box<Expr>> },
84 Interval { interval: Vec<Box<Expr>> },
85 Timestamp { timestamp: Box<Expr> },
86 Date { date: Box<Expr> },
87 Property { property: String },
88 BBox { bbox: Vec<Box<Expr>> },
89 Float(f64),
90 Literal(String),
91 Bool(bool),
92 Array(Vec<Box<Expr>>),
93 Geometry(Geometry),
94 Null,
95}
96impl TryFrom<Value> for Expr {
97 type Error = Error;
98 fn try_from(v: Value) -> Result<Expr, Error> {
99 serde_json::from_value(v).map_err(Error::from)
100 }
101}
102impl TryFrom<Expr> for Value {
103 type Error = Error;
104 fn try_from(v: Expr) -> Result<Value, Error> {
105 serde_json::to_value(v).map_err(Error::from)
106 }
107}
108impl TryFrom<Expr> for f64 {
109 type Error = Error;
110 fn try_from(v: Expr) -> Result<f64, Error> {
111 match v {
112 Expr::Float(v) => Ok(v),
113 Expr::Literal(v) => f64::from_str(&v).map_err(Error::from),
114 _ => Err(Error::ExprToF64(v)),
115 }
116 }
117}
118
119impl TryFrom<&Expr> for bool {
120 type Error = Error;
121 fn try_from(v: &Expr) -> Result<bool, Error> {
122 match v {
123 Expr::Bool(v) => Ok(*v),
124 Expr::Literal(v) => bool::from_str(v).map_err(Error::from),
125 _ => Err(Error::ExprToBool(v.clone())),
126 }
127 }
128}
129
130impl TryFrom<Expr> for String {
131 type Error = Error;
132 fn try_from(v: Expr) -> Result<String, Error> {
133 match v {
134 Expr::Literal(v) => Ok(v),
135 Expr::Bool(v) => Ok(v.to_string()),
136 Expr::Float(v) => Ok(v.to_string()),
137 _ => Err(Error::ExprToBool(v)),
138 }
139 }
140}
141
142impl TryFrom<Expr> for GGeom {
143 type Error = Error;
144 fn try_from(v: Expr) -> Result<GGeom, Error> {
145 match v {
146 Expr::Geometry(v) => Ok(GGeom::try_from_wkt_str(&v.to_wkt().unwrap())
147 .expect("Failed to convert WKT to Geometry")),
148 Expr::BBox { ref bbox } => {
149 let minx: f64 = bbox[0].as_ref().clone().try_into()?;
150 let miny: f64 = bbox[1].as_ref().clone().try_into()?;
151 let maxx: f64;
152 let maxy: f64;
153
154 match bbox.len() {
155 4 => {
156 maxx = bbox[2].as_ref().clone().try_into()?;
157 maxy = bbox[3].as_ref().clone().try_into()?;
158 }
159 6 => {
160 maxx = bbox[3].as_ref().clone().try_into()?;
161 maxy = bbox[4].as_ref().clone().try_into()?;
162 }
163 _ => return Err(Error::ExprToGeom(v.clone())),
164 };
165 let rec = Rect::new(coord! {x:minx, y:miny}, coord! {x:maxx,y:maxy});
166 Ok(rec.into())
167 }
168 _ => Err(Error::ExprToGeom(v)),
169 }
170 }
171}
172
173impl TryFrom<Expr> for HashSet<String> {
174 type Error = Error;
175 fn try_from(v: Expr) -> Result<HashSet<String>, Error> {
176 match v {
177 Expr::Array(v) => {
178 let mut h = HashSet::new();
179 for el in v {
180 let _ = h.insert(el.to_text()?);
181 }
182 Ok(h)
183 }
184 _ => Err(Error::ExprToGeom(v)),
185 }
186 }
187}
188
189fn cmp_op<T: PartialEq + PartialOrd>(left: T, right: T, op: &str) -> Result<Expr, Error> {
190 let out = match op {
191 "=" => Ok(left == right),
192 "<=" => Ok(left <= right),
193 "<" => Ok(left < right),
194 ">=" => Ok(left >= right),
195 ">" => Ok(left > right),
196 "<>" => Ok(left != right),
197 _ => Err(Error::OpNotImplemented("Binary Bool")),
198 };
199 match out {
200 Ok(v) => Ok(Expr::Bool(v)),
201 _ => Err(Error::OperationError()),
202 }
203}
204
205fn arith_op(left: Expr, right: Expr, op: &str) -> Result<Expr, Error> {
206 let left = f64::try_from(left)?;
207 let right = f64::try_from(right)?;
208 let out = match op {
209 "+" => Ok(left + right),
210 "-" => Ok(left - right),
211 "*" => Ok(left * right),
212 "/" => Ok(left / right),
213 "%" => Ok(left % right),
214 "^" => Ok(left.powf(right)),
215 _ => Err(Error::OpNotImplemented("Arith")),
216 };
217 match out {
218 Ok(v) => Ok(Expr::Float(v)),
219 _ => Err(Error::OperationError()),
220 }
221}
222
223fn array_op(left: Expr, right: Expr, op: &str) -> Result<Expr, Error> {
224 let left: HashSet<String> = left.try_into()?;
225 let right: HashSet<String> = right.try_into()?;
226 let out = match op {
227 "a_equals" => Ok(left == right),
228 "a_contains" => Ok(left.is_superset(&right)),
229 "a_containedby" => Ok(left.is_subset(&right)),
230 "a_overlaps" => Ok(!left.is_disjoint(&right)),
231 _ => Err(Error::OpNotImplemented("Arith")),
232 };
233 match out {
234 Ok(v) => Ok(Expr::Bool(v)),
235 _ => Err(Error::OperationError()),
236 }
237}
238
239impl Expr {
240 pub fn reduce(self, j: Option<&Value>) -> Result<Expr, Error> {
268 match self {
269 Expr::Property { ref property } => {
270 if let Some(j) = j {
271 if let Some(value) = j.dot_get::<Value>(property)? {
272 Expr::try_from(value)
273 } else if let Some(value) =
274 j.dot_get::<Value>(&format!("properties.{}", property))?
275 {
276 Expr::try_from(value)
277 } else {
278 Ok(self)
279 }
280 } else {
281 Ok(self)
282 }
283 }
284 Expr::Interval { ref interval } => {
285 let start = interval[0].as_ref().clone().reduce(j)?;
286 let end = interval[1].as_ref().clone().reduce(j)?;
287 Ok(Expr::Interval {
288 interval: vec![Box::new(start), Box::new(end)],
289 })
290 }
291 Expr::Operation { op, args } => {
292 let op = op.clone().to_lowercase();
293
294 let args: Vec<Box<Expr>> = args
295 .into_iter()
296 .map(|expr| expr.reduce(j).map(Box::new))
297 .collect::<Result<_, _>>()?;
298
299 if op == "isnull" {
300 match args[0].as_ref() {
301 Expr::Null => Ok(Expr::Bool(true)),
302 Expr::Property { property: _ } => Ok(Expr::Bool(true)),
303 _ => Ok(Expr::Bool(false)),
304 }
305 } else if args.iter().any(|arg| matches!(arg.as_ref(), Expr::Null)) {
306 Ok(Expr::Bool(false))
307 } else if BOOLOPS.contains(&op.as_str()) {
308 let curop = op.clone();
309 let mut dedupargs: Vec<Box<Expr>> = vec![];
310 let mut nestedargs: Vec<Box<Expr>> = vec![];
311 for a in args {
312 match *a {
313 Expr::Operation { op, args } if op == curop => {
314 nestedargs.append(&mut args.clone());
315 }
316 _ => {
317 dedupargs.push(a.clone());
318 }
319 }
320 }
321 dedupargs.append(&mut nestedargs);
322 dedupargs.sort_by(|a, b| a.partial_cmp(b).unwrap());
323 dedupargs.dedup();
324
325 let mut anytrue: bool = false;
326 let mut anyfalse: bool = false;
327 let mut anyexp: bool = false;
328
329 for a in dedupargs.iter() {
330 let b = bool::try_from(a.as_ref());
331 match b {
332 Ok(true) => {
333 anytrue = true;
334 }
335 Ok(false) => {
336 anyfalse = true;
337 }
338 _ => {
339 anyexp = true;
340 }
341 }
342 }
343 if op == "and" && anytrue {
344 dedupargs.retain(|x| !bool::try_from(x.as_ref()).unwrap_or(false));
345 }
346 if dedupargs.len() == 1 {
347 Ok(dedupargs.pop().unwrap().as_ref().clone())
348 } else if (op == "and" && anyfalse) || (op == "or" && !anytrue && !anyexp) {
349 Ok(Expr::Bool(false))
350 } else if (op == "and" && !anyfalse && !anyexp) || (op == "or" && anytrue) {
351 Ok(Expr::Bool(true))
352 } else {
353 Ok(Expr::Operation {
354 op,
355 args: dedupargs.clone(),
356 })
357 }
358 } else if op == "not" {
359 match args[0].deref() {
360 Expr::Bool(v) => Ok(Expr::Bool(!v)),
361 _ => Ok(Expr::Operation { op, args }),
362 }
363 } else if op == "casei" {
364 match args[0].as_ref() {
365 Expr::Literal(v) => Ok(Expr::Literal(v.to_lowercase())),
366 _ => Ok(Expr::Operation { op, args }),
367 }
368 } else if op == "accenti" {
369 match args[0].as_ref() {
370 Expr::Literal(v) => Ok(Expr::Literal(unaccent(v))),
371 _ => Ok(Expr::Operation { op, args }),
372 }
373 } else if op == "between" {
374 Ok(Expr::Bool(args[0] >= args[1] && args[0] <= args[2]))
375 } else if args.len() != 2 {
376 Ok(Expr::Operation { op, args })
377 } else {
378 let mut left = args[0].deref().clone();
380 let mut right = args[1].deref().clone();
381
382 match (&left, &right) {
384 (Expr::Date { .. }, Expr::Literal(ref v)) => {
385 right = Expr::Date {
386 date: Box::new(Expr::Literal(v.clone())),
387 };
388 }
389 (Expr::Timestamp { .. }, Expr::Literal(ref v)) => {
390 right = Expr::Timestamp {
391 timestamp: Box::new(Expr::Literal(v.clone())),
392 };
393 }
394 (Expr::Literal(ref v), Expr::Date { .. }) => {
395 left = Expr::Date {
396 date: Box::new(Expr::Literal(v.clone())),
397 };
398 }
399 (Expr::Literal(ref v), Expr::Timestamp { .. }) => {
400 left = Expr::Timestamp {
401 timestamp: Box::new(Expr::Literal(v.clone())),
402 };
403 }
404 _ => {}
405 }
406
407 if TEMPORALOPS.contains(&op.as_str()) {
408 Ok(temporal_op(left, right, &op)
409 .unwrap_or_else(|_| Expr::Operation { op, args }))
410 } else if (matches!(left, Expr::Date { .. } | Expr::Timestamp { .. })
412 && matches!(right, Expr::Date { .. } | Expr::Timestamp { .. })
413 && (EQOPS.contains(&op.as_str()) || CMPOPS.contains(&op.as_str())))
414 {
415 let l_dr = crate::temporal::DateRange::try_from(left.clone())?;
417 let r_dr = crate::temporal::DateRange::try_from(right.clone())?;
418 let cmp = match op.as_str() {
419 "=" => l_dr == r_dr,
420 "<=" => l_dr <= r_dr,
421 "<" => l_dr < r_dr,
422 ">=" => l_dr >= r_dr,
423 ">" => l_dr > r_dr,
424 "<>" => l_dr != r_dr,
425 _ => unreachable!(),
426 };
427 Ok(Expr::Bool(cmp))
428 } else if std::mem::discriminant(&left) == std::mem::discriminant(&right) {
429 if SPATIALOPS.contains(&op.as_str()) {
430 Ok(spatial_op(left, right, &op)
431 .unwrap_or_else(|_| Expr::Operation { op, args }))
432 } else if ARITHOPS.contains(&op.as_str()) {
433 Ok(arith_op(left, right, &op)
434 .unwrap_or_else(|_| Expr::Operation { op, args }))
435 } else if EQOPS.contains(&op.as_str()) || CMPOPS.contains(&op.as_str()) {
436 Ok(cmp_op(left, right, &op)
437 .unwrap_or_else(|_| Expr::Operation { op, args }))
438 } else if ARRAYOPS.contains(&op.as_str()) {
439 Ok(array_op(left, right, &op)
440 .unwrap_or_else(|_| Expr::Operation { op, args }))
441 } else if op == "like" {
442 let l: String = left.try_into()?;
443 let r: String = right.try_into()?;
444 let m: bool = Like::<true>::like(l.as_str(), r.as_str())?;
445 Ok(Expr::Bool(m))
446 } else {
447 Ok(Expr::Operation { op, args })
448 }
449 } else if op == "in" {
450 let l: String = left.to_text()?;
451 let r: HashSet<String> = right.try_into()?;
452 let isin: bool = r.contains(&l);
453 Ok(Expr::Bool(isin))
454 } else {
455 Ok(Expr::Operation { op, args })
456 }
457 }
458 }
459 _ => Ok(self),
460 }
461 }
462
463 pub fn matches(self, j: Option<&Value>) -> Result<bool, Error> {
479 let reduced = self.reduce(j)?;
480
481 match reduced {
482 Expr::Bool(v) => Ok(v),
483 _ => Err(Error::NonReduced()),
484 }
485 }
486
487 pub fn is_true(self) -> bool {
489 matches!(self, Expr::Bool(true))
490 }
491
492 pub fn filter<'a, I>(&self, items: I) -> Result<Vec<&'a Value>, Error>
512 where
513 I: IntoIterator<Item = &'a Value>,
514 {
515 let mut filtered = Vec::new();
516 for item in items {
517 let e = self.clone().reduce(Some(item))?;
518 if e.is_true() {
519 filtered.push(item)
520 }
521 }
522 Ok(filtered)
523 }
524
525 pub fn to_text(&self) -> Result<String, Error> {
536 macro_rules! check_len {
537 ($name:expr, $args:expr, $len:expr, $text:expr) => {
538 if $args.len() == $len {
539 Ok($text)
540 } else {
541 Err(Error::InvalidNumberOfArguments {
542 name: $name.to_string(),
543 actual: $args.len(),
544 expected: $len,
545 })
546 }
547 };
548 }
549
550 match self {
551 Expr::Bool(v) => Ok(v.to_string()),
552 Expr::Float(v) => Ok(v.to_string()),
553 Expr::Literal(v) => Ok(quote_literal(v).to_string()),
554 Expr::Property { property } => Ok(quote_identifier(property).to_string()),
555 Expr::Null => Ok("NULL".to_string()),
556 Expr::Interval { interval } => {
557 check_len!(
558 "interval",
559 interval,
560 2,
561 format!(
562 "INTERVAL({},{})",
563 interval[0].to_text()?,
564 interval[1].to_text()?
565 )
566 )
567 }
568 Expr::Date { date } => Ok(format!("DATE({})", date.to_text()?)),
569 Expr::Timestamp { timestamp } => Ok(format!("TIMESTAMP({})", timestamp.to_text()?)),
570 Expr::Geometry(v) => v.to_wkt(),
571 Expr::Array(v) => {
572 let array_els: Vec<String> =
573 v.iter().map(|a| a.to_text()).collect::<Result<_, _>>()?;
574 Ok(format!("({})", array_els.join(", ")))
575 }
576 Expr::Operation { op, args } => {
577 let a: Vec<String> = args.iter().map(|x| x.to_text()).collect::<Result<_, _>>()?;
578 match op.as_str() {
579 "and" => Ok(format!("({})", a.join(" AND "))),
580 "or" => Ok(format!("({})", a.join(" OR "))),
581 "like" => Ok(format!("({} LIKE {})", a[0], a[1])),
582 "in" => Ok(format!("({} IN {})", a[0], a[1])),
583 "between" => {
584 check_len!(
585 "between",
586 a,
587 3,
588 format!("({} BETWEEN {} AND {})", a[0], a[1], a[2])
589 )
590 }
591 "not" => {
592 check_len!("not", a, 1, format!("(NOT {})", a[0]))
593 }
594 "isNull" => {
595 check_len!("is null", a, 1, format!("({} IS NULL)", a[0]))
596 }
597 "+" | "-" | "*" | "/" | "%" => {
598 let paddedop = format!(" {} ", op);
599 Ok(a.join(&paddedop).to_string())
600 }
601 "^" | "=" | "<=" | "<" | "<>" | ">" | ">=" => {
602 check_len!(op, a, 2, format!("({} {} {})", a[0], op, a[1]))
603 }
604 _ => Ok(format!("{}({})", quote_identifier(op), a.join(", "))),
605 }
606 }
607 Expr::BBox { bbox } => {
608 let array_els: Vec<String> =
609 bbox.iter().map(|a| a.to_text()).collect::<Result<_, _>>()?;
610 Ok(format!("BBOX({})", array_els.join(", ")))
611 }
612 }
613 }
614
615 pub fn to_json(&self) -> Result<String, Error> {
626 serde_json::to_string(&self).map_err(Error::from)
627 }
628
629 pub fn to_json_pretty(&self) -> Result<String, Error> {
640 serde_json::to_string_pretty(&self).map_err(Error::from)
641 }
642
643 pub fn to_value(&self) -> Result<Value, Error> {
654 serde_json::to_value(self).map_err(Error::from)
655 }
656
657 pub fn is_valid(&self) -> bool {
674 let value = serde_json::to_value(self);
675 match &value {
676 Ok(value) => {
677 let validator = Validator::new().expect("Could not create default validator");
678 validator.is_valid(value)
679 }
680 _ => false,
681 }
682 }
683}
684
685impl FromStr for Expr {
686 type Err = Error;
687
688 fn from_str(s: &str) -> Result<Expr, Error> {
689 if s.starts_with('{') {
690 crate::parse_json(s).map_err(Error::from)
691 } else {
692 crate::parse_text(s)
693 }
694 }
695}
696
697impl Add for Expr {
698 type Output = Expr;
699
700 fn add(self, other: Expr) -> Expr {
725 Expr::Operation {
726 op: "and".to_string(),
727 args: vec![Box::new(self), Box::new(other)],
728 }
729 }
730}
731#[cfg(test)]
732mod tests {
733 use super::Expr;
734
735 #[test]
736 fn keep_z() {
737 let point: Expr = "POINT Z(-105.1019 40.1672 4981)".parse().unwrap();
738 assert_eq!("POINT Z(-105.1019 40.1672 4981)", point.to_text().unwrap());
739 }
740
741 #[test]
742 fn implicit_z() {
743 let point: Expr = "POINT (-105.1019 40.1672 4981)".parse().unwrap();
744 assert_eq!("POINT Z(-105.1019 40.1672 4981)", point.to_text().unwrap());
745 }
746
747 #[test]
748 fn keep_m() {
749 let point: Expr = "POINT M(-105.1019 40.1672 42)".parse().unwrap();
750 assert_eq!("POINT M(-105.1019 40.1672 42)", point.to_text().unwrap());
751 }
752
753 #[test]
754 fn keep_zm() {
755 let point: Expr = "POINT ZM(-105.1019 40.1672 4981 42)".parse().unwrap();
756 assert_eq!(
757 "POINT ZM(-105.1019 40.1672 4981 42)",
758 point.to_text().unwrap()
759 );
760 }
761
762 #[test]
763 fn keep_one_element_lists() {
764 let expr: Expr = "ogc_fid IN ('1')".parse().unwrap();
766 assert_eq!(expr.to_text().unwrap(), "(ogc_fid IN ('1'))");
767 }
768}