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}