426 lines
12 KiB
Rust
426 lines
12 KiB
Rust
/*
|
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
|
*/
|
|
|
|
use crate::{
|
|
object::JmapObject,
|
|
request::deserialize::{DeserializeArguments, deserialize_request},
|
|
types::state::State,
|
|
};
|
|
use serde::{
|
|
Deserialize, Deserializer,
|
|
de::{self, DeserializeSeed, MapAccess, SeqAccess, Visitor},
|
|
};
|
|
use std::{
|
|
borrow::Cow,
|
|
fmt::{self, Display, Formatter},
|
|
};
|
|
use store::fts::{FilterItem, FilterType, FtsFilter};
|
|
use types::{id::Id};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct QueryRequest<T: JmapObject> {
|
|
pub account_id: Id,
|
|
pub filter: Vec<Filter<T::Filter>>,
|
|
pub sort: Option<Vec<Comparator<T::Comparator>>>,
|
|
pub position: Option<i32>,
|
|
pub anchor: Option<Id>,
|
|
pub anchor_offset: Option<i32>,
|
|
pub limit: Option<usize>,
|
|
pub calculate_total: Option<bool>,
|
|
pub arguments: T::QueryArguments,
|
|
}
|
|
|
|
#[derive(Debug, Clone, serde::Serialize)]
|
|
pub struct QueryResponse {
|
|
#[serde(rename = "accountId")]
|
|
pub account_id: Id,
|
|
|
|
#[serde(rename = "queryState")]
|
|
pub query_state: State,
|
|
|
|
#[serde(rename = "canCalculateChanges")]
|
|
pub can_calculate_changes: bool,
|
|
|
|
#[serde(rename = "position")]
|
|
pub position: i32,
|
|
|
|
#[serde(rename = "ids")]
|
|
pub ids: Vec<Id>,
|
|
|
|
#[serde(rename = "total")]
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub total: Option<usize>,
|
|
|
|
#[serde(rename = "limit")]
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub limit: Option<usize>,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum Filter<T>
|
|
where
|
|
T: for<'de> DeserializeArguments<'de> + Default,
|
|
{
|
|
Property(T),
|
|
And,
|
|
Or,
|
|
Not,
|
|
Close,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
|
pub struct Comparator<T>
|
|
where
|
|
T: for<'de> DeserializeArguments<'de> + Default,
|
|
{
|
|
pub is_ascending: bool,
|
|
pub collation: Option<String>,
|
|
pub property: T,
|
|
}
|
|
|
|
impl<'de, T: JmapObject> DeserializeArguments<'de> for QueryRequest<T> {
|
|
fn deserialize_argument<A>(&mut self, key: &str, map: &mut A) -> Result<(), A::Error>
|
|
where
|
|
A: serde::de::MapAccess<'de>,
|
|
{
|
|
hashify::fnc_map!(key.as_bytes(),
|
|
b"accountId" => {
|
|
self.account_id = map.next_value()?;
|
|
},
|
|
b"filter" => {
|
|
self.filter = map.next_value::<FilterWrapper<T::Filter>>()?.0;
|
|
},
|
|
b"sort" => {
|
|
self.sort = map.next_value()?;
|
|
},
|
|
b"calculateTotal" => {
|
|
self.calculate_total = map.next_value()?;
|
|
},
|
|
b"position" => {
|
|
self.position = map.next_value()?;
|
|
},
|
|
b"anchor" => {
|
|
self.anchor = map.next_value()?;
|
|
},
|
|
b"anchorOffset" => {
|
|
self.anchor_offset = map.next_value()?;
|
|
},
|
|
b"limit" => {
|
|
self.limit = map.next_value()?;
|
|
},
|
|
_ => {
|
|
self.arguments.deserialize_argument(key, map)?;
|
|
}
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<'de, T: JmapObject> Deserialize<'de> for QueryRequest<T> {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
deserialize_request(deserializer)
|
|
}
|
|
}
|
|
|
|
impl<T: JmapObject> Default for QueryRequest<T> {
|
|
fn default() -> Self {
|
|
Self {
|
|
account_id: Id::default(),
|
|
filter: vec![],
|
|
sort: None,
|
|
position: None,
|
|
anchor: None,
|
|
anchor_offset: None,
|
|
limit: None,
|
|
calculate_total: None,
|
|
arguments: T::QueryArguments::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
struct FilterMapCollector<'x, T: 'x>(&'x mut Vec<Filter<T>>)
|
|
where
|
|
T: for<'de> DeserializeArguments<'de> + Default;
|
|
|
|
struct FilterListCollector<'x, T: 'x>(&'x mut Vec<Filter<T>>)
|
|
where
|
|
T: for<'de> DeserializeArguments<'de> + Default;
|
|
|
|
pub(super) struct FilterWrapper<T>(pub Vec<Filter<T>>)
|
|
where
|
|
T: for<'de> DeserializeArguments<'de> + Default;
|
|
|
|
impl<'de, T> Deserialize<'de> for FilterWrapper<T>
|
|
where
|
|
T: for<'de2> DeserializeArguments<'de2> + Default,
|
|
{
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
let mut items = Vec::new();
|
|
FilterMapCollector(&mut items)
|
|
.deserialize(deserializer)
|
|
.map(|_| FilterWrapper(items))
|
|
}
|
|
}
|
|
|
|
impl<'de, 'x, T> DeserializeSeed<'de> for FilterMapCollector<'x, T>
|
|
where
|
|
T: for<'de2> DeserializeArguments<'de2> + Default,
|
|
{
|
|
type Value = ();
|
|
|
|
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
struct FilterVisitor<'x, T: 'x>(&'x mut Vec<Filter<T>>)
|
|
where
|
|
T: for<'de2> DeserializeArguments<'de2> + Default;
|
|
|
|
impl<'de, 'x, T> Visitor<'de> for FilterVisitor<'x, T>
|
|
where
|
|
T: for<'de2> DeserializeArguments<'de2> + Default,
|
|
{
|
|
type Value = ();
|
|
|
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(formatter, "a filter object")
|
|
}
|
|
|
|
fn visit_map<V>(self, mut map: V) -> Result<(), V::Error>
|
|
where
|
|
V: MapAccess<'de>,
|
|
{
|
|
let mut filter = T::default();
|
|
let mut has_filter = false;
|
|
let mut has_conditions = None;
|
|
let mut op = None;
|
|
|
|
while let Some(key) = map.next_key::<Cow<str>>()? {
|
|
match key.len() {
|
|
8 if key == "operator" => {
|
|
let op_ = hashify::tiny_map!(
|
|
map.next_value::<&str>()?.as_bytes(),
|
|
"AND" => Filter::And,
|
|
"OR" => Filter::Or,
|
|
"NOT" => Filter::Not,
|
|
)
|
|
.ok_or_else(|| {
|
|
de::Error::custom(format!("Unknown filter operator: {}", key))
|
|
})?;
|
|
|
|
if let Some(pos) = has_conditions {
|
|
self.0[pos] = op_;
|
|
} else {
|
|
op = Some(op_);
|
|
}
|
|
}
|
|
10 if key == "conditions" => {
|
|
has_conditions = Some(self.0.len());
|
|
self.0.push(op.take().unwrap_or(Filter::And));
|
|
map.next_value_seed(FilterListCollector(self.0))?;
|
|
self.0.push(Filter::Close);
|
|
}
|
|
_ => {
|
|
filter.deserialize_argument(&key, &mut map)?;
|
|
has_filter = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if has_filter {
|
|
if has_conditions.is_some() {
|
|
return Err(de::Error::custom(
|
|
"Cannot mix conditions with property filters",
|
|
));
|
|
}
|
|
|
|
self.0.push(Filter::Property(filter));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
deserializer.deserialize_map(FilterVisitor(self.0))
|
|
}
|
|
}
|
|
|
|
impl<'de, 'x, T> DeserializeSeed<'de> for FilterListCollector<'x, T>
|
|
where
|
|
T: for<'de2> DeserializeArguments<'de2> + Default,
|
|
{
|
|
type Value = ();
|
|
|
|
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
struct FilterVisitor<'x, T: 'x>(&'x mut Vec<Filter<T>>)
|
|
where
|
|
T: for<'de2> DeserializeArguments<'de2> + Default;
|
|
|
|
impl<'de, 'x, T> Visitor<'de> for FilterVisitor<'x, T>
|
|
where
|
|
T: for<'de2> DeserializeArguments<'de2> + Default,
|
|
{
|
|
type Value = ();
|
|
|
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(formatter, "a filter list")
|
|
}
|
|
|
|
fn visit_seq<A>(self, mut seq: A) -> Result<(), A::Error>
|
|
where
|
|
A: SeqAccess<'de>,
|
|
{
|
|
while let Some(()) = seq.next_element_seed(FilterMapCollector(self.0))? {}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
deserializer.deserialize_seq(FilterVisitor(self.0))
|
|
}
|
|
}
|
|
|
|
impl<'de, T> DeserializeArguments<'de> for Comparator<T>
|
|
where
|
|
T: for<'de2> DeserializeArguments<'de2> + Default,
|
|
{
|
|
fn deserialize_argument<A>(&mut self, key: &str, map: &mut A) -> Result<(), A::Error>
|
|
where
|
|
A: serde::de::MapAccess<'de>,
|
|
{
|
|
hashify::fnc_map!(key.as_bytes(),
|
|
b"isAscending" => {
|
|
self.is_ascending = map.next_value()?;
|
|
},
|
|
b"collation" => {
|
|
self.collation = map.next_value()?;
|
|
},
|
|
_ => {
|
|
self.property.deserialize_argument(key, map)?;
|
|
}
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<'de, T> Deserialize<'de> for Comparator<T>
|
|
where
|
|
T: for<'de2> DeserializeArguments<'de2> + Default,
|
|
{
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
deserialize_request(deserializer)
|
|
}
|
|
}
|
|
|
|
impl<T> From<Filter<T>> for store::query::Filter
|
|
where
|
|
T: for<'de> DeserializeArguments<'de> + Default,
|
|
{
|
|
fn from(value: Filter<T>) -> Self {
|
|
match value {
|
|
Filter::And => Self::And,
|
|
Filter::Or => Self::Or,
|
|
Filter::Not => Self::Not,
|
|
Filter::Close => Self::End,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: Into<u8> + Display + Clone + std::fmt::Debug, U> From<Filter<U>> for FtsFilter<T>
|
|
where
|
|
U: for<'de> DeserializeArguments<'de> + Default,
|
|
{
|
|
fn from(value: Filter<U>) -> Self {
|
|
match value {
|
|
Filter::And => Self::And,
|
|
Filter::Or => Self::Or,
|
|
Filter::Not => Self::Not,
|
|
Filter::Close => Self::End,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> From<FilterType> for Filter<T>
|
|
where
|
|
T: for<'de> DeserializeArguments<'de> + Default,
|
|
{
|
|
fn from(value: FilterType) -> Self {
|
|
match value {
|
|
FilterType::And => Filter::And,
|
|
FilterType::Or => Filter::Or,
|
|
FilterType::Not => Filter::Not,
|
|
FilterType::End => Filter::Close,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> Comparator<T>
|
|
where
|
|
T: for<'de> DeserializeArguments<'de> + Default,
|
|
{
|
|
pub fn descending(property: T) -> Self {
|
|
Self {
|
|
property,
|
|
is_ascending: false,
|
|
collation: None,
|
|
}
|
|
}
|
|
|
|
pub fn ascending(property: T) -> Self {
|
|
Self {
|
|
property,
|
|
is_ascending: true,
|
|
collation: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> FilterItem for Filter<T>
|
|
where
|
|
T: for<'de> DeserializeArguments<'de> + FilterItem + Default,
|
|
{
|
|
fn filter_type(&self) -> FilterType {
|
|
match self {
|
|
Filter::And => FilterType::And,
|
|
Filter::Or => FilterType::Or,
|
|
Filter::Not => FilterType::Not,
|
|
Filter::Close => FilterType::End,
|
|
Filter::Property(p) => p.filter_type(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> Display for Filter<T>
|
|
where
|
|
T: for<'de> DeserializeArguments<'de> + Display + Default,
|
|
{
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Filter::And => write!(f, "and"),
|
|
Filter::Or => write!(f, "or"),
|
|
Filter::Not => write!(f, "not"),
|
|
Filter::Close => write!(f, "close"),
|
|
Filter::Property(p) => write!(f, "{}", p),
|
|
}
|
|
}
|
|
}
|