Files
Stalwart/crates/jmap-proto/src/method/query_changes.rs
2025-06-15 15:09:32 +02:00

147 lines
4.7 KiB
Rust

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::format_compact;
use crate::{
parser::{Ignore, JsonObjectParser, Token, json::Parser},
request::{RequestProperty, RequestPropertyParser, method::MethodObject},
types::{id::Id, state::State},
};
use super::query::{Comparator, Filter, RequestArguments, parse_filter, parse_sort};
#[derive(Debug, Clone)]
pub struct QueryChangesRequest {
pub account_id: Id,
pub filter: Vec<Filter>,
pub sort: Option<Vec<Comparator>>,
pub since_query_state: State,
pub max_changes: Option<usize>,
pub up_to_id: Option<Id>,
pub calculate_total: Option<bool>,
pub arguments: RequestArguments,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct QueryChangesResponse {
#[serde(rename = "accountId")]
pub account_id: Id,
#[serde(rename = "oldQueryState")]
pub old_query_state: State,
#[serde(rename = "newQueryState")]
pub new_query_state: State,
#[serde(rename = "total")]
#[serde(skip_serializing_if = "Option::is_none")]
pub total: Option<usize>,
#[serde(rename = "removed")]
pub removed: Vec<Id>,
#[serde(rename = "added")]
pub added: Vec<AddedItem>,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct AddedItem {
pub id: Id,
pub index: usize,
}
impl AddedItem {
pub fn new(id: Id, index: usize) -> Self {
Self { id, index }
}
}
impl JsonObjectParser for QueryChangesRequest {
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized,
{
let mut request = QueryChangesRequest {
arguments: match &parser.ctx {
MethodObject::Email => RequestArguments::Email(Default::default()),
MethodObject::Mailbox => RequestArguments::Mailbox(Default::default()),
MethodObject::EmailSubmission => RequestArguments::EmailSubmission,
MethodObject::Quota => RequestArguments::Quota,
_ => {
return Err(trc::JmapEvent::UnknownMethod
.into_err()
.details(format_compact!("{}/queryChanges", parser.ctx)));
}
},
filter: vec![],
sort: None,
calculate_total: None,
account_id: Id::default(),
since_query_state: State::Initial,
max_changes: None,
up_to_id: None,
};
parser
.next_token::<String>()?
.assert_jmap(Token::DictStart)?;
while let Some(key) = parser.next_dict_key::<RequestProperty>()? {
match &key.hash[0] {
0x0064_4974_6e75_6f63_6361 => {
request.account_id = parser.next_token::<Id>()?.unwrap_string("accountId")?;
}
0x7265_746c_6966 => match parser.next_token::<Ignore>()? {
Token::DictStart => {
request.filter = parse_filter(parser)?;
}
Token::Null => (),
token => {
return Err(token.error("filter", "object or null"));
}
},
0x7472_6f73 => match parser.next_token::<Ignore>()? {
Token::ArrayStart => {
request.sort = parse_sort(parser)?.into();
}
Token::Null => (),
token => {
return Err(token.error("sort", "array or null"));
}
},
0x0065_7461_7453_7972_6575_5165_636e_6973 => {
request.since_query_state = parser
.next_token::<State>()?
.unwrap_string("sinceQueryState")?;
}
0x7365_676e_6168_4378_616d => {
request.max_changes = parser
.next_token::<Ignore>()?
.unwrap_usize_or_null("maxChanges")?;
}
0x6449_6f54_7075 => {
request.up_to_id =
parser.next_token::<Id>()?.unwrap_string_or_null("upToId")?;
}
0x6c61_746f_5465_7461_6c75_636c_6163 => {
request.calculate_total = parser
.next_token::<Ignore>()?
.unwrap_bool_or_null("calculateTotal")?;
}
_ => {
if !request.arguments.parse(parser, key)? {
parser.skip_token(parser.depth_array, parser.depth_dict)?;
}
}
}
}
Ok(request)
}
}