ogc_cql2/lib.rs
1// SPDX-License-Identifier: Apache-2.0
2
3#![warn(missing_docs)]
4
5//! OGC CQL2 parser and runtime interpreter...
6//!
7//! # Third-party crates
8//!
9//! This project relies on few 3<sup>rd</sup> party crates to satisfy the requirements of
10//! the CQL2 standard. These are...
11//!
12//! 1. Geometry
13//! * [`geos`][1]: Rust bindings for [GEOS][2] C API for handling geometries.
14//!
15//! 2. JSON Deserialization:
16//! * [serde][3]: for the basic capabilities.
17//! * [serde_json][4]: for the JSON format bindings.
18//! * [serde_with][5]: for custom helpers.
19//!
20//! 3. Date + Time:
21//! * [jiff][6]: for time-zone-aware date and timestamp handling.
22//!
23//! 4. Case + Accent Insensitive Strings:
24//! * [unicase][7]: for comparing strings when case is not important.
25//! * [unicode-normalization][8]: for un-accenting strings w/ Unicode
26//! decomposition.
27//!
28//! 5. CRS Transformation:
29//! * [proj][9]: for coordinate transformation via bindings to the [PROJ][10]
30//! API.
31//!
32//!
33//!
34//! [1]: https://crates.io/crates/geos
35//! [2]: https://libgeos.org/
36//! [3]: https://crates.io/crates/serde
37//! [4]: https://crates.io/crates/serde_json
38//! [5]: https://crates.io/crates/serde_with
39//! [6]: https://crates.io/crates/jiff
40//! [7]: https://crates.io/crates/unicase
41//! [8]: https://crates.io/crates/unicode-normalization
42//! [9]: https://crates.io/crates/proj
43//! [10]: https://proj.org/
44//!
45
46#![doc = include_str!("../doc/FUNCTION.md")]
47#![doc = include_str!("../doc/CONFIGURATION.md")]
48
49mod bound;
50mod config;
51mod context;
52mod crs;
53mod error;
54mod evaluator;
55mod expr;
56mod function;
57mod geom;
58mod json;
59mod op;
60mod qstring;
61mod queryable;
62mod text;
63
64pub mod prelude;
65
66use crate::{expr::E, text::cql2::expression};
67use core::fmt;
68pub use error::MyError;
69use std::collections::HashMap;
70
71pub use bound::*;
72pub use context::*;
73pub use crs::*;
74pub use evaluator::*;
75pub use function::*;
76pub use geom::*;
77pub use qstring::QString;
78pub use queryable::*;
79
80#[derive(Debug)]
81/// An instance of an OGC CQL2 filter.
82pub enum Expression {
83 /// Instance generated from a valid text-encoded input string.
84 Text(TextEncoded),
85 /// Instance generated from a valid JSON-encoded input string.
86 Json(Box<JsonEncoded>),
87}
88
89impl Expression {
90 /// Try to construct from a text-encoded string.
91 pub fn try_from_text(s: &str) -> Result<Self, MyError> {
92 let x = expression(s).map_err(MyError::Text)?;
93 Ok(Expression::Text(TextEncoded(x)))
94 }
95
96 /// Try to construct from a JSON-encoded string.
97 pub fn try_from_json(s: &str) -> Result<Self, MyError> {
98 let x = serde_json::from_str::<json::Expression>(s).map_err(MyError::Json)?;
99 Ok(Expression::Json(Box::new(JsonEncoded(x))))
100 }
101
102 /// Return a reference to the text-encoded variant as an `Option`.
103 pub fn as_text_encoded(&self) -> Option<&TextEncoded> {
104 match self {
105 Expression::Text(x) => Some(x),
106 Expression::Json(_) => None,
107 }
108 }
109}
110
111impl fmt::Display for Expression {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 match self {
114 Expression::Text(x) => write!(f, "{}", x.0),
115 Expression::Json(x) => write!(f, "{}", x.0),
116 }
117 }
118}
119
120/// Text-encoded CQL2 Expression.
121#[derive(Debug, PartialEq)]
122pub struct TextEncoded(expr::E);
123
124/// JSON-encoded CQL2 Expression.
125#[derive(Debug)]
126pub struct JsonEncoded(json::Expression);
127
128/// Possible outcome values when evaluating an [Expression] against an
129/// individual _Resource_ from a collection.
130///
131/// From [OGC CQL2][1]:
132/// > _Each resource instance in the source collection is evaluated against
133/// > a filtering expression. The net effect of evaluating a filter
134/// > [Expression] is a subset of resources that satisfy the predicate(s)
135/// > in the [Expression]._
136///
137/// Logically connected predicates are evaluated according to the following
138/// truth table, where `T` is TRUE, `F` is FALSE and `N` is NULL.
139/// ```text
140/// +-----+-----+---------+---------+
141/// | P1 | P2 | P1 & P2 | P1 | P2 |
142/// +-----+-----+---------+---------+
143/// | T | T | T | T |
144/// | T | F | F | T |
145/// | F | T | F | T |
146/// | F | F | F | F |
147/// | T | N | N | T |
148/// | F | N | F | N |
149/// | N | T | N | T |
150/// | N | F | F | N |
151/// | N | N | N | N |
152/// +-----+-----+---------+---------+
153/// ```
154/// [1]: https://docs.ogc.org/is/21-065r2/21-065r2.html
155#[derive(Debug, PartialEq, Eq)]
156pub enum Outcome {
157 /// The input satisfies the [Expression] and should be marked as being in
158 /// the result set.
159 T,
160 /// The input does not satisfy the filter [Expression] and should not be
161 /// included the result set.
162 F,
163 /// Likewise.
164 N,
165}
166
167impl fmt::Display for Outcome {
168 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169 match self {
170 Outcome::T => write!(f, "T"),
171 Outcome::F => write!(f, "F",),
172 Outcome::N => write!(f, "N"),
173 }
174 }
175}
176
177impl Outcome {
178 /// Constructor from an optional boolean.
179 pub fn new(flag: Option<&bool>) -> Self {
180 match flag {
181 Some(b) => match b {
182 true => Self::T,
183 false => Self::F,
184 },
185 None => Self::N,
186 }
187 }
188}
189
190/// A dictionary of queryable property names (strings) to [Q] values.
191/// [Queryables][Q] have same lifetime as their parent.
192pub type Resource = HashMap<String, Q>;
193
194/// Internal Queryable type variants.
195#[derive(Debug)]
196pub(crate) enum DataType {
197 /// A Unicode UTF-8 string.
198 Str,
199 /// A numeric value including integers and floating points.
200 Num,
201 /// A boolean value.
202 Bool,
203 /// An _Instant_ with a granularity of a second or smaller. Timestamps are
204 /// always in the time zone UTC ("Z").
205 Timestamp,
206 /// An _Instant_ with a granularity of a day. Dates are local without an
207 /// associated time zone.
208 Date,
209 /// A temporal range of 2 _Instants_ each either _fixed_ or _unbounded_.
210 #[allow(dead_code)]
211 Interval,
212 /// A spatial (geometry) value.
213 Geom,
214 /// A collection of homogeneous values.
215 #[allow(dead_code)]
216 List,
217}