Files
Stalwart/crates/jmap-proto/src/request/parser.rs
Maurus Decimus 7564f51642 Telemetry fixes
2026-04-12 18:52:18 +02:00

885 lines
38 KiB
Rust

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use super::{
Call, Request, RequestMethod,
method::{MethodFunction, MethodName, MethodObject},
};
use crate::request::{
CopyRequestMethod, GetRequestMethod, ParseRequestMethod, QueryChangesRequestMethod,
QueryRequestMethod, SetRequestMethod,
deserialize::{DeserializeArguments, deserialize_request},
};
use serde::{
Deserialize, Deserializer,
de::{self, SeqAccess, Visitor},
};
use std::fmt::{self, Display};
impl<'x> Request<'x> {
pub fn parse(json: &'x [u8], max_calls: usize, max_size: usize) -> trc::Result<Self> {
if json.len() <= max_size {
match serde_json::from_slice::<Request>(json) {
Ok(request) => {
if request.method_calls.len() <= max_calls {
Ok(request)
} else {
Err(trc::LimitEvent::CallsIn.into_err())
}
}
Err(err) => Err(trc::JmapEvent::NotRequest
.into_err()
.reason(err.to_string())
.details(String::from_utf8_lossy(json).into_owned())),
}
} else {
Err(trc::LimitEvent::SizeRequest.into_err())
}
}
}
impl<'de> DeserializeArguments<'de> for Request<'de> {
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"using" => {
self.using = map.next_value()?;
},
b"methodCalls" => {
self.method_calls = map.next_value()?;
},
b"createdIds" => {
self.created_ids = map.next_value()?;
},
_ => {
let _ = map.next_value::<serde::de::IgnoredAny>()?;
}
);
Ok(())
}
}
struct CallVisitor;
impl<'de> Visitor<'de> for CallVisitor {
type Value = Call<RequestMethod<'de>>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an array with 3 elements")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Call<RequestMethod<'de>>, V::Error>
where
V: SeqAccess<'de>,
{
let method_name = seq
.next_element::<&str>()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let name = match MethodName::parse(method_name) {
Some(name) => name,
None => {
// Ignore the rest of the call
let _ = seq
.next_element::<serde::de::IgnoredAny>()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let id = seq
.next_element::<String>()?
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
return Ok(Call {
id,
method: RequestMethod::Error(
trc::JmapEvent::UnknownMethod
.into_err()
.details(method_name.to_string()),
),
name: MethodName::error(),
});
}
};
let method = match (&name.fnc, &name.obj) {
(MethodFunction::Get, MethodObject::Email) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::Email(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::Mailbox) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::Mailbox(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::Thread) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::Thread(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::Identity) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::Identity(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::EmailSubmission) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::EmailSubmission(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::PushSubscription) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::PushSubscription(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::VacationResponse) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::VacationResponse(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::SieveScript) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::Sieve(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::Principal) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::Principal(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::Quota) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::Quota(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::Blob) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::Blob(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::Calendar) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::Calendar(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::CalendarEvent) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::CalendarEvent(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::CalendarEventNotification) => {
match seq.next_element() {
Ok(Some(value)) => {
RequestMethod::Get(GetRequestMethod::CalendarEventNotification(value))
}
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
}
}
(MethodFunction::Get, MethodObject::ParticipantIdentity) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::ParticipantIdentity(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::AddressBook) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::AddressBook(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::ContactCard) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::ContactCard(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::FileNode) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::FileNode(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::ShareNotification) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::ShareNotification(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::SearchSnippet) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::SearchSnippet(value),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::Registry(_)) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::Registry(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::Email) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::Email(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::Mailbox) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::Mailbox(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::Identity) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::Identity(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::EmailSubmission) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::EmailSubmission(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::PushSubscription) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::PushSubscription(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::VacationResponse) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::VacationResponse(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::SieveScript) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::Sieve(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::Calendar) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::Calendar(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::CalendarEvent) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::CalendarEvent(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::CalendarEventNotification) => {
match seq.next_element() {
Ok(Some(value)) => {
RequestMethod::Set(SetRequestMethod::CalendarEventNotification(value))
}
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
}
}
(MethodFunction::Set, MethodObject::ParticipantIdentity) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::ParticipantIdentity(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::AddressBook) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::AddressBook(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::ContactCard) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::ContactCard(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::FileNode) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::FileNode(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::ShareNotification) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::ShareNotification(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::Registry(_)) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::Registry(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Query, MethodObject::Email) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Query(QueryRequestMethod::Email(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Query, MethodObject::Mailbox) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Query(QueryRequestMethod::Mailbox(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Query, MethodObject::EmailSubmission) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Query(QueryRequestMethod::EmailSubmission(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Query, MethodObject::SieveScript) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Query(QueryRequestMethod::Sieve(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Query, MethodObject::Principal) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Query(QueryRequestMethod::Principal(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Query, MethodObject::Quota) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Query(QueryRequestMethod::Quota(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Query, MethodObject::Calendar) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Query(QueryRequestMethod::Calendar(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Query, MethodObject::CalendarEvent) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Query(QueryRequestMethod::CalendarEvent(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Query, MethodObject::CalendarEventNotification) => {
match seq.next_element() {
Ok(Some(value)) => {
RequestMethod::Query(QueryRequestMethod::CalendarEventNotification(value))
}
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
}
}
(MethodFunction::Query, MethodObject::AddressBook) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Query(QueryRequestMethod::AddressBook(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Query, MethodObject::ContactCard) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Query(QueryRequestMethod::ContactCard(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Query, MethodObject::FileNode) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Query(QueryRequestMethod::FileNode(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Query, MethodObject::ShareNotification) => match seq.next_element() {
Ok(Some(value)) => {
RequestMethod::Query(QueryRequestMethod::ShareNotification(value))
}
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Query, MethodObject::Registry(_)) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Query(QueryRequestMethod::Registry(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::QueryChanges, MethodObject::Email) => match seq.next_element() {
Ok(Some(value)) => {
RequestMethod::QueryChanges(QueryChangesRequestMethod::Email(value))
}
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::QueryChanges, MethodObject::Mailbox) => match seq.next_element() {
Ok(Some(value)) => {
RequestMethod::QueryChanges(QueryChangesRequestMethod::Mailbox(value))
}
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::QueryChanges, MethodObject::EmailSubmission) => {
match seq.next_element() {
Ok(Some(value)) => RequestMethod::QueryChanges(
QueryChangesRequestMethod::EmailSubmission(value),
),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
}
}
(MethodFunction::QueryChanges, MethodObject::Principal) => match seq.next_element() {
Ok(Some(value)) => {
RequestMethod::QueryChanges(QueryChangesRequestMethod::Principal(value))
}
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::QueryChanges, MethodObject::Quota) => match seq.next_element() {
Ok(Some(value)) => {
RequestMethod::QueryChanges(QueryChangesRequestMethod::Quota(value))
}
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::QueryChanges, MethodObject::CalendarEvent) => match seq.next_element()
{
Ok(Some(value)) => {
RequestMethod::QueryChanges(QueryChangesRequestMethod::CalendarEvent(value))
}
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::QueryChanges, MethodObject::CalendarEventNotification) => {
match seq.next_element() {
Ok(Some(value)) => RequestMethod::QueryChanges(
QueryChangesRequestMethod::CalendarEventNotification(value),
),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
}
}
(MethodFunction::QueryChanges, MethodObject::ContactCard) => match seq.next_element() {
Ok(Some(value)) => {
RequestMethod::QueryChanges(QueryChangesRequestMethod::ContactCard(value))
}
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::QueryChanges, MethodObject::FileNode) => match seq.next_element() {
Ok(Some(value)) => {
RequestMethod::QueryChanges(QueryChangesRequestMethod::FileNode(value))
}
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::QueryChanges, MethodObject::ShareNotification) => {
match seq.next_element() {
Ok(Some(value)) => RequestMethod::QueryChanges(
QueryChangesRequestMethod::ShareNotification(value),
),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
}
}
(MethodFunction::Changes, _) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Changes(value),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Copy, MethodObject::Email) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Copy(CopyRequestMethod::Email(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Copy, MethodObject::Blob) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Copy(CopyRequestMethod::Blob(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Copy, MethodObject::CalendarEvent) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Copy(CopyRequestMethod::CalendarEvent(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Copy, MethodObject::ContactCard) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Copy(CopyRequestMethod::ContactCard(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Lookup, MethodObject::Blob) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::LookupBlob(value),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Upload, MethodObject::Blob) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::UploadBlob(value),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Import, MethodObject::Email) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::ImportEmail(value),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Parse, MethodObject::Email) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Parse(ParseRequestMethod::Email(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Parse, MethodObject::CalendarEvent) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Parse(ParseRequestMethod::CalendarEvent(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Parse, MethodObject::ContactCard) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Parse(ParseRequestMethod::ContactCard(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::GetAvailability, MethodObject::Principal) => {
match seq.next_element() {
Ok(Some(value)) => {
RequestMethod::Get(GetRequestMethod::PrincipalAvailability(value))
}
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
}
}
(MethodFunction::Validate, MethodObject::SieveScript) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::ValidateScript(value),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Echo, MethodObject::Core) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Echo(value),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
_ => {
return Err(de::Error::custom(format!(
"Invalid method function/object combination: {}",
method_name
)));
}
};
let id = seq
.next_element::<String>()?
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
Ok(Call { id, method, name })
}
}
impl RequestMethod<'_> {
fn invalid(err: impl Display) -> Self {
RequestMethod::Error(
trc::JmapEvent::InvalidArguments
.into_err()
.details(err.to_string()),
)
}
}
impl<'de> Deserialize<'de> for Request<'de> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserialize_request(deserializer)
}
}
impl<'de> Deserialize<'de> for Call<RequestMethod<'de>> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_seq(CallVisitor)
}
}
#[cfg(test)]
mod tests {
use crate::request::Request;
const TEST: &str = r#"
{
"using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ],
"methodCalls": [
[ "method1", {
"arg1": "arg1data",
"arg2": "arg2data"
}, "c1" ],
[ "Core/echo", {
"hello": true,
"high": 5
}, "c2" ],
[ "method3", {"hello": [{"a": {"b": true}}]}, "c3" ]
],
"createdIds": {
"c1": "m1",
"c2": "m2"
}
}
"#;
const TEST1: &str = r#"
{
"using": [
"urn:ietf:params:jmap:core",
"urn:ietf:params:jmap:mail"
],
"methodCalls": [
[
"Email/query",
{
"accountId": "0",
"filter": { "conditions": [ { "hasKeyword": "music", "maxSize": 455 }, { "hasKeyword": "video" }, { "operator": "AND", "conditions": [ { "subject": "test" }, { "minSize": 100 } ] } ], "operator": "OR" },
"sort": [
{
"property": "subject",
"isAscending": true
},
{
"property": "allInThreadHaveKeyword",
"isAscending": false,
"keyword": "$seen"
},
{
"keyword": "$junk",
"property": "someInThreadHaveKeyword",
"collation": "i;octet",
"isAscending": false
}
],
"position": 0,
"limit": 10
},
"c1"
]
],
"createdIds": {}
}
"#;
const TEST2: &str = r##"
{
"using": [
"urn:ietf:params:jmap:submission",
"urn:ietf:params:jmap:mail",
"urn:ietf:params:jmap:core"
],
"methodCalls": [
[
"Email/set",
{
"accountId": "c",
"create": {
"c37ee58b-e224-4799-88e6-1d7484e3b782": {
"mailboxIds": {
"9": true
},
"subject": "test",
"from": [
{
"name": "Foo",
"email": "foo@bar.com"
}
],
"to": [
{
"name": null,
"email": "bar@foo.com"
}
],
"cc": [],
"bcc": [],
"replyTo": [
{
"name": null,
"email": "foo@bar.com"
}
],
"htmlBody": [
{
"partId": "c37ee58b-e224-4799-88e6-1d7484e3b782",
"type": "text/html"
}
],
"bodyValues": {
"c37ee58b-e224-4799-88e6-1d7484e3b782": {
"value": "<p>test email<br></p>",
"isEncodingProblem": false,
"isTruncated": false
}
},
"header:User-Agent:asText": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0"
}
}
},
"c0"
],
[
"EmailSubmission/set",
{
"accountId": "c",
"create": {
"c37ee58b-e224-4799-88e6-1d7484e3b782": {
"identityId": "a",
"emailId": "#c37ee58b-e224-4799-88e6-1d7484e3b782",
"envelope": {
"mailFrom": {
"email": "foo@bar.com"
},
"rcptTo": [
{
"email": "bar@foo.com"
}
]
}
}
},
"onSuccessUpdateEmail": {
"#c37ee58b-e224-4799-88e6-1d7484e3b782": {
"mailboxIds/d": true,
"mailboxIds/9": null,
"keywords/$seen": true,
"keywords/$draft": null
}
}
},
"c1"
]
]
}
"##;
#[test]
fn parse_request() {
println!("{:#?}", Request::parse(TEST.as_bytes(), 10, 10240));
println!("{:#?}", Request::parse(TEST1.as_bytes(), 10, 10240));
println!("{:#?}", Request::parse(TEST2.as_bytes(), 10, 10240));
}
}