JMAP protocol layer refactoring (part 2)
This commit is contained in:
@@ -4,12 +4,15 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{object::JmapObject, request::method::MethodObject, types::state::State};
|
||||
use compact_str::format_compact;
|
||||
use jmap_tools::Property;
|
||||
use crate::{
|
||||
object::JmapObject,
|
||||
request::deserialize::{DeserializeArguments, deserialize_request},
|
||||
types::state::State,
|
||||
};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use types::id::Id;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ChangesRequest {
|
||||
pub account_id: Id,
|
||||
pub since_state: State,
|
||||
@@ -41,71 +44,40 @@ pub struct ChangesResponse<T: JmapObject> {
|
||||
pub updated_properties: Option<Vec<T::Property>>,
|
||||
}
|
||||
|
||||
/*#[derive(Debug, Clone, serde::Serialize)]
|
||||
pub enum RequestArguments {
|
||||
Email,
|
||||
Mailbox,
|
||||
Thread,
|
||||
Identity,
|
||||
EmailSubmission,
|
||||
Quota,
|
||||
}*/
|
||||
|
||||
impl JsonObjectParser for ChangesRequest {
|
||||
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
|
||||
impl<'de> DeserializeArguments<'de> for ChangesRequest {
|
||||
fn deserialize_argument<A>(&mut self, key: &str, map: &mut A) -> Result<(), A::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
A: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut request = ChangesRequest {
|
||||
arguments: match &parser.ctx {
|
||||
MethodObject::Email => RequestArguments::Email,
|
||||
MethodObject::Mailbox => RequestArguments::Mailbox,
|
||||
MethodObject::Thread => RequestArguments::Thread,
|
||||
MethodObject::Identity => RequestArguments::Identity,
|
||||
MethodObject::EmailSubmission => RequestArguments::EmailSubmission,
|
||||
MethodObject::Quota => RequestArguments::Quota,
|
||||
_ => {
|
||||
return Err(trc::JmapEvent::UnknownMethod
|
||||
.into_err()
|
||||
.details(format_compact!("{}/changes", parser.ctx)));
|
||||
}
|
||||
hashify::fnc_map!(key.as_bytes(),
|
||||
b"accountId" => {
|
||||
self.account_id = map.next_value()?;
|
||||
},
|
||||
account_id: Id::default(),
|
||||
since_state: State::Initial,
|
||||
max_changes: 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")?;
|
||||
}
|
||||
0x6574_6174_5365_636e_6973 => {
|
||||
request.since_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")?;
|
||||
}
|
||||
|
||||
_ => {
|
||||
parser.skip_token(parser.depth_array, parser.depth_dict)?;
|
||||
}
|
||||
b"sinceQueryState" => {
|
||||
self.since_state = map.next_value()?;
|
||||
},
|
||||
b"maxChanges" => {
|
||||
self.max_changes = map.next_value()?;
|
||||
},
|
||||
_ => {
|
||||
let _ = map.next_value::<serde::de::IgnoredAny>()?;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Ok(request)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ChangesResponse {
|
||||
impl<'de> Deserialize<'de> for ChangesRequest {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: JmapObject> ChangesResponse<T> {
|
||||
pub fn has_changes(&self) -> bool {
|
||||
!self.created.is_empty() || !self.updated.is_empty() || !self.destroyed.is_empty()
|
||||
}
|
||||
|
||||
@@ -7,12 +7,15 @@
|
||||
use crate::{
|
||||
error::set::SetError,
|
||||
object::{JmapObject, blob::BlobProperty},
|
||||
request::{MaybeInvalid, method::MethodObject, reference::MaybeIdReference},
|
||||
request::{
|
||||
MaybeInvalid,
|
||||
deserialize::{DeserializeArguments, deserialize_request},
|
||||
reference::MaybeIdReference,
|
||||
},
|
||||
types::state::State,
|
||||
};
|
||||
use compact_str::format_compact;
|
||||
use jmap_tools::Value;
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use types::{blob::BlobId, id::Id};
|
||||
use utils::map::vec_map::VecMap;
|
||||
|
||||
@@ -27,11 +30,6 @@ pub struct CopyRequest<'x, T: JmapObject> {
|
||||
pub destroy_from_if_in_state: Option<State>,
|
||||
}
|
||||
|
||||
/*#[derive(Debug, Clone)]
|
||||
pub enum RequestArguments {
|
||||
Email,
|
||||
}*/
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
pub struct CopyResponse<T: JmapObject> {
|
||||
#[serde(rename = "fromAccountId")]
|
||||
@@ -55,7 +53,7 @@ pub struct CopyResponse<T: JmapObject> {
|
||||
pub not_created: VecMap<MaybeInvalid<Id>, SetError<T::Property>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct CopyBlobRequest {
|
||||
pub from_account_id: Id,
|
||||
pub account_id: Id,
|
||||
@@ -79,109 +77,94 @@ pub struct CopyBlobResponse {
|
||||
pub not_copied: VecMap<MaybeInvalid<BlobId>, SetError<BlobProperty>>,
|
||||
}
|
||||
|
||||
impl JsonObjectParser for CopyRequest<RequestArguments> {
|
||||
fn parse(parser: &mut Parser) -> trc::Result<Self>
|
||||
impl<'de, T: JmapObject> DeserializeArguments<'de> for CopyRequest<'de, T> {
|
||||
fn deserialize_argument<A>(&mut self, key: &str, map: &mut A) -> Result<(), A::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
A: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut request = CopyRequest {
|
||||
arguments: match &parser.ctx {
|
||||
MethodObject::Email => RequestArguments::Email,
|
||||
_ => {
|
||||
return Err(trc::JmapEvent::UnknownMethod
|
||||
.into_err()
|
||||
.details(format_compact!("{}/copy", parser.ctx)));
|
||||
}
|
||||
hashify::fnc_map!(key.as_bytes(),
|
||||
b"accountId" => {
|
||||
self.account_id = map.next_value()?;
|
||||
},
|
||||
account_id: Id::default(),
|
||||
if_in_state: None,
|
||||
b"ifInState" => {
|
||||
self.if_in_state = map.next_value()?;
|
||||
},
|
||||
b"fromAccountId" => {
|
||||
self.from_account_id = map.next_value()?;
|
||||
},
|
||||
b"ifFromInState" => {
|
||||
self.if_from_in_state = map.next_value()?;
|
||||
},
|
||||
b"create" => {
|
||||
self.create = map.next_value()?;
|
||||
},
|
||||
b"onSuccessDestroyOriginal" => {
|
||||
self.on_success_destroy_original = map.next_value()?;
|
||||
},
|
||||
b"destroyFromIfInState" => {
|
||||
self.destroy_from_if_in_state = map.next_value()?;
|
||||
},
|
||||
_ => {
|
||||
let _ = map.next_value::<serde::de::IgnoredAny>()?;
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> DeserializeArguments<'de> for CopyBlobRequest {
|
||||
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"fromAccountId" => {
|
||||
self.from_account_id = map.next_value()?;
|
||||
},
|
||||
b"blobIds" => {
|
||||
self.blob_ids = map.next_value()?;
|
||||
},
|
||||
_ => {
|
||||
let _ = map.next_value::<serde::de::IgnoredAny>()?;
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T: JmapObject> Deserialize<'de> for CopyRequest<'de, T> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for CopyBlobRequest {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T: JmapObject> Default for CopyRequest<'de, T> {
|
||||
fn default() -> Self {
|
||||
CopyRequest {
|
||||
from_account_id: Id::default(),
|
||||
if_from_in_state: None,
|
||||
create: VecMap::default(),
|
||||
account_id: Id::default(),
|
||||
if_in_state: None,
|
||||
create: VecMap::new(),
|
||||
on_success_destroy_original: None,
|
||||
destroy_from_if_in_state: 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")?;
|
||||
}
|
||||
0x6574_6165_7263 => {
|
||||
request.create =
|
||||
<VecMap<MaybeReference<Id, String>, Value<'x, P, E>>>::parse(parser)?;
|
||||
}
|
||||
0x0064_4974_6e75_6f63_6341_6d6f_7266 => {
|
||||
request.from_account_id =
|
||||
parser.next_token::<Id>()?.unwrap_string("fromAccountId")?;
|
||||
}
|
||||
0x0065_7461_7453_6e49_6d6f_7246_6669 => {
|
||||
request.if_from_in_state = parser
|
||||
.next_token::<State>()?
|
||||
.unwrap_string_or_null("ifFromInState")?;
|
||||
}
|
||||
0x796f_7274_7365_4473_7365_6363_7553_6e6f => {
|
||||
request.on_success_destroy_original = parser
|
||||
.next_token::<String>()?
|
||||
.unwrap_bool_or_null("onSuccessDestroyOriginal")?;
|
||||
}
|
||||
0x536e_4966_496d_6f72_4679_6f72_7473_6564 => {
|
||||
request.destroy_from_if_in_state = parser
|
||||
.next_token::<State>()?
|
||||
.unwrap_string_or_null("destroyFromIfInState")?;
|
||||
}
|
||||
0x0065_7461_7453_6e49_6669 => {
|
||||
request.if_in_state = parser
|
||||
.next_token::<State>()?
|
||||
.unwrap_string_or_null("ifInState")?;
|
||||
}
|
||||
_ => {
|
||||
parser.skip_token(parser.depth_array, parser.depth_dict)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonObjectParser for CopyBlobRequest {
|
||||
fn parse(parser: &mut Parser) -> trc::Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let mut request = CopyBlobRequest {
|
||||
account_id: Id::default(),
|
||||
from_account_id: Id::default(),
|
||||
blob_ids: Vec::new(),
|
||||
};
|
||||
|
||||
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")?;
|
||||
}
|
||||
0x0064_4974_6e75_6f63_6341_6d6f_7266 => {
|
||||
request.from_account_id =
|
||||
parser.next_token::<Id>()?.unwrap_string("fromAccountId")?;
|
||||
}
|
||||
0x0073_6449_626f_6c62 => {
|
||||
request.blob_ids = <Vec<BlobId>>::parse(parser)?;
|
||||
}
|
||||
_ => {
|
||||
parser.skip_token(parser.depth_array, parser.depth_dict)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(request)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,39 +8,23 @@ use crate::{
|
||||
object::JmapObject,
|
||||
request::{
|
||||
MaybeInvalid,
|
||||
method::MethodObject,
|
||||
deserialize::{DeserializeArguments, deserialize_request},
|
||||
reference::{MaybeIdReference, MaybeResultReference, ResultReference},
|
||||
},
|
||||
types::state::State,
|
||||
};
|
||||
use compact_str::format_compact;
|
||||
use jmap_tools::{Property, Value};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use types::{blob::BlobId, id::Id};
|
||||
|
||||
#[derive(Debug, Clone, serde::Deserialize)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GetRequest<T: JmapObject> {
|
||||
pub account_id: Id,
|
||||
pub ids: Option<MaybeResultReference<Vec<MaybeIdReference<T::Id>>>>,
|
||||
pub properties: Option<MaybeResultReference<Vec<T::Property>>>,
|
||||
#[serde(flatten)]
|
||||
pub properties: Option<MaybeResultReference<Vec<MaybeInvalid<T::Property>>>>,
|
||||
pub arguments: T::GetArguments,
|
||||
}
|
||||
|
||||
/*#[derive(Debug, Clone)]
|
||||
pub enum RequestArguments {
|
||||
Email(email::GetArguments),
|
||||
Mailbox,
|
||||
Thread,
|
||||
Identity,
|
||||
EmailSubmission,
|
||||
PushSubscription,
|
||||
SieveScript,
|
||||
VacationResponse,
|
||||
Principal,
|
||||
Quota,
|
||||
Blob(blob::GetArguments),
|
||||
}*/
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
pub struct GetResponse<T: JmapObject> {
|
||||
#[serde(rename = "accountId")]
|
||||
@@ -56,107 +40,59 @@ pub struct GetResponse<T: JmapObject> {
|
||||
pub not_found: Vec<MaybeInvalid<T::Id>>,
|
||||
}
|
||||
|
||||
impl JsonObjectParser for GetRequest<RequestArguments> {
|
||||
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
|
||||
impl<'de, T: JmapObject> DeserializeArguments<'de> for GetRequest<T> {
|
||||
fn deserialize_argument<A>(&mut self, key: &str, map: &mut A) -> Result<(), A::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
A: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut request = GetRequest {
|
||||
arguments: match &parser.ctx {
|
||||
MethodObject::Email => RequestArguments::Email(Default::default()),
|
||||
MethodObject::Mailbox => RequestArguments::Mailbox,
|
||||
MethodObject::Thread => RequestArguments::Thread,
|
||||
MethodObject::Identity => RequestArguments::Identity,
|
||||
MethodObject::EmailSubmission => RequestArguments::EmailSubmission,
|
||||
MethodObject::PushSubscription => RequestArguments::PushSubscription,
|
||||
MethodObject::SieveScript => RequestArguments::SieveScript,
|
||||
MethodObject::VacationResponse => RequestArguments::VacationResponse,
|
||||
MethodObject::Principal => RequestArguments::Principal,
|
||||
MethodObject::Blob => RequestArguments::Blob(Default::default()),
|
||||
MethodObject::Quota => RequestArguments::Quota,
|
||||
_ => {
|
||||
return Err(trc::JmapEvent::UnknownMethod
|
||||
.into_err()
|
||||
.details(format_compact!("{}/get", parser.ctx)));
|
||||
}
|
||||
hashify::fnc_map!(key.as_bytes(),
|
||||
b"accountId" => {
|
||||
self.account_id = map.next_value()?;
|
||||
},
|
||||
b"ids" => {
|
||||
self.ids = map.next_value::<Option<Vec<MaybeIdReference<T::Id>>>>()?.map(MaybeResultReference::Value);
|
||||
},
|
||||
b"properties" => {
|
||||
self.properties = map.next_value::<Option<Vec<MaybeInvalid<T::Property>>>>()?.map(MaybeResultReference::Value);
|
||||
},
|
||||
b"#ids" => {
|
||||
self.ids = Some(MaybeResultReference::Reference(map.next_value::<ResultReference>()?));
|
||||
},
|
||||
b"#properties" => {
|
||||
self.properties = Some(MaybeResultReference::Reference(map.next_value::<ResultReference>()?));
|
||||
},
|
||||
_ => {
|
||||
self.arguments.deserialize_argument(key, map)?;
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T: JmapObject> Deserialize<'de> for GetRequest<T> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: JmapObject> Default for GetRequest<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
account_id: Id::default(),
|
||||
ids: None,
|
||||
properties: 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 if !key.is_ref => {
|
||||
request.account_id = parser.next_token::<Id>()?.unwrap_string("accountId")?;
|
||||
}
|
||||
0x0073_6469 => {
|
||||
request.ids = if !key.is_ref {
|
||||
if parser.ctx != MethodObject::Blob {
|
||||
<Option<Vec<MaybeReference<Id, String>>>>::parse(parser)?.map(|ids| {
|
||||
MaybeReference::Value(ids.into_iter().map(Into::into).collect())
|
||||
})
|
||||
} else {
|
||||
<Option<Vec<MaybeReference<BlobId, String>>>>::parse(parser)?.map(
|
||||
|ids| {
|
||||
MaybeReference::Value(ids.into_iter().map(Into::into).collect())
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Some(MaybeReference::Reference(ResultReference::parse(parser)?))
|
||||
};
|
||||
}
|
||||
0x7365_6974_7265_706f_7270 => {
|
||||
request.properties = if !key.is_ref {
|
||||
<Option<Vec<Property>>>::parse(parser)?.map(MaybeReference::Value)
|
||||
} else {
|
||||
Some(MaybeReference::Reference(ResultReference::parse(parser)?))
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
if !request.arguments.parse(parser, key)? {
|
||||
parser.skip_token(parser.depth_array, parser.depth_dict)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl RequestPropertyParser for RequestArguments {
|
||||
fn parse(&mut self, parser: &mut Parser, property: RequestProperty) -> trc::Result<bool> {
|
||||
match self {
|
||||
RequestArguments::Email(arguments) => arguments.parse(parser, property),
|
||||
RequestArguments::Blob(arguments) => arguments.parse(parser, property),
|
||||
_ => Ok(false),
|
||||
arguments: T::GetArguments::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GetRequest<RequestArguments> {
|
||||
pub fn take_arguments(&mut self) -> RequestArguments {
|
||||
std::mem::replace(&mut self.arguments, RequestArguments::VacationResponse)
|
||||
}
|
||||
|
||||
pub fn with_arguments<T>(self, arguments: T) -> GetRequest<T> {
|
||||
GetRequest {
|
||||
arguments,
|
||||
account_id: self.account_id,
|
||||
ids: self.ids,
|
||||
properties: self.properties,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> GetRequest<T> {
|
||||
pub fn unwrap_properties(&mut self, default: &[Property]) -> Vec<Property> {
|
||||
/*
|
||||
impl<T: JmapObject> GetRequest<T> {
|
||||
pub fn unwrap_properties(&mut self, default: &[T::Property]) -> Vec<T::Property> {
|
||||
if let Some(mut properties) = self.properties.take().map(|p| p.unwrap()) {
|
||||
// Add Id Property
|
||||
if !properties.contains(&Property::Id) {
|
||||
@@ -168,7 +104,7 @@ impl<T> GetRequest<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_ids(&mut self, max_objects_in_get: usize) -> trc::Result<Option<Vec<Id>>> {
|
||||
pub fn unwrap_ids(&mut self, max_objects_in_get: usize) -> trc::Result<Option<Vec<T::Id>>> {
|
||||
if let Some(ids) = self.ids.take() {
|
||||
let ids = ids.unwrap();
|
||||
if ids.len() <= max_objects_in_get {
|
||||
@@ -205,3 +141,4 @@ impl<T> GetRequest<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -7,24 +7,29 @@
|
||||
use crate::{
|
||||
error::set::SetError,
|
||||
object::email::{EmailProperty, EmailValue},
|
||||
request::reference::{MaybeIdReference, MaybeResultReference, ResultReference},
|
||||
request::{
|
||||
MaybeInvalid,
|
||||
deserialize::{DeserializeArguments, deserialize_request},
|
||||
reference::{MaybeIdReference, MaybeResultReference, ResultReference},
|
||||
},
|
||||
response::Response,
|
||||
types::{date::UTCDate, state::State},
|
||||
};
|
||||
use jmap_tools::Value;
|
||||
use jmap_tools::{Key, Value};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use types::{blob::BlobId, id::Id, keyword::Keyword};
|
||||
use utils::map::vec_map::VecMap;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ImportEmailRequest {
|
||||
pub account_id: Id,
|
||||
pub if_in_state: Option<State>,
|
||||
pub emails: VecMap<String, ImportEmail>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ImportEmail {
|
||||
pub blob_id: BlobId,
|
||||
pub blob_id: MaybeInvalid<BlobId>,
|
||||
pub mailbox_ids: MaybeResultReference<Vec<MaybeIdReference<Id>>>,
|
||||
pub keywords: Vec<Keyword>,
|
||||
pub received_at: Option<UTCDate>,
|
||||
@@ -51,96 +56,85 @@ pub struct ImportEmailResponse {
|
||||
pub not_created: VecMap<String, SetError<EmailProperty>>,
|
||||
}
|
||||
|
||||
impl JsonObjectParser for ImportEmailRequest {
|
||||
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
|
||||
impl<'de> DeserializeArguments<'de> for ImportEmailRequest {
|
||||
fn deserialize_argument<A>(&mut self, key: &str, map: &mut A) -> Result<(), A::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
A: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut request = ImportEmailRequest {
|
||||
account_id: Id::default(),
|
||||
if_in_state: None,
|
||||
emails: VecMap::new(),
|
||||
};
|
||||
|
||||
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 if !key.is_ref => {
|
||||
request.account_id = parser.next_token::<Id>()?.unwrap_string("accountId")?;
|
||||
}
|
||||
0x0065_7461_7453_6e49_6669 if !key.is_ref => {
|
||||
request.if_in_state = parser
|
||||
.next_token::<State>()?
|
||||
.unwrap_string_or_null("ifInState")?;
|
||||
}
|
||||
0x736c_6961_6d65 if !key.is_ref => {
|
||||
request.emails = <VecMap<String, ImportEmail>>::parse(parser)?;
|
||||
}
|
||||
_ => {
|
||||
parser.skip_token(parser.depth_array, parser.depth_dict)?;
|
||||
}
|
||||
hashify::fnc_map!(key.as_bytes(),
|
||||
b"accountId" => {
|
||||
self.account_id = map.next_value()?;
|
||||
},
|
||||
b"ifInState" => {
|
||||
self.if_in_state = map.next_value()?;
|
||||
},
|
||||
b"emails" => {
|
||||
self.emails = map.next_value()?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let _ = map.next_value::<serde::de::IgnoredAny>()?;
|
||||
}
|
||||
);
|
||||
|
||||
Ok(request)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonObjectParser for ImportEmail {
|
||||
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
|
||||
impl<'de> DeserializeArguments<'de> for ImportEmail {
|
||||
fn deserialize_argument<A>(&mut self, key: &str, map: &mut A) -> Result<(), A::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
A: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut request = ImportEmail {
|
||||
blob_id: BlobId::default(),
|
||||
mailbox_ids: MaybeReference::Value(vec![]),
|
||||
keywords: vec![],
|
||||
received_at: None,
|
||||
};
|
||||
|
||||
parser
|
||||
.next_token::<String>()?
|
||||
.assert_jmap(Token::DictStart)?;
|
||||
|
||||
while let Some(key) = parser.next_dict_key::<RequestProperty>()? {
|
||||
match &key.hash[0] {
|
||||
0x6449_626f_6c62 if !key.is_ref => {
|
||||
request.blob_id = parser.next_token::<BlobId>()?.unwrap_string("blobId")?;
|
||||
}
|
||||
0x7364_4978_6f62_6c69_616d => {
|
||||
request.mailbox_ids = if !key.is_ref {
|
||||
MaybeReference::Value(
|
||||
<SetValueMap<MaybeReference<Id, String>>>::parse(parser)?.values,
|
||||
)
|
||||
} else {
|
||||
MaybeReference::Reference(ResultReference::parse(parser)?)
|
||||
};
|
||||
}
|
||||
0x7364_726f_7779_656b if !key.is_ref => {
|
||||
request.keywords = <SetValueMap<Keyword>>::parse(parser)?.values;
|
||||
}
|
||||
0x7441_6465_7669_6563_6572 if !key.is_ref => {
|
||||
request.received_at = parser
|
||||
.next_token::<UTCDate>()?
|
||||
.unwrap_string_or_null("receivedAt")?;
|
||||
}
|
||||
_ => {
|
||||
parser.skip_token(parser.depth_array, parser.depth_dict)?;
|
||||
}
|
||||
hashify::fnc_map!(key.as_bytes(),
|
||||
b"blobId" => {
|
||||
self.blob_id = map.next_value()?;
|
||||
},
|
||||
b"keywords" => {
|
||||
self.keywords = map.next_value()?;
|
||||
},
|
||||
b"receivedAt" => {
|
||||
self.received_at = map.next_value()?;
|
||||
},
|
||||
b"mailboxIds" => {
|
||||
self.mailbox_ids = MaybeResultReference::Value(map.next_value::<Vec<MaybeIdReference<Id>>>()?);
|
||||
},
|
||||
b"#mailboxIds" => {
|
||||
self.mailbox_ids = MaybeResultReference::Reference(map.next_value::<ResultReference>()?);
|
||||
},
|
||||
_ => {
|
||||
let _ = map.next_value::<serde::de::IgnoredAny>()?;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Ok(request)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ImportEmail {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ImportEmailRequest {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl ImportEmailResponse {
|
||||
pub fn update_created_ids(&self, response: &mut Response) {
|
||||
for (user_id, obj) in &self.created {
|
||||
if let Some(id) = obj.get(&Property::Id).as_id() {
|
||||
if let Value::Object(obj) = obj
|
||||
&& let Some(Value::Element(EmailValue::Id(id))) =
|
||||
obj.get(&Key::Property(EmailProperty::Id))
|
||||
{
|
||||
response.created_ids.insert(user_id.clone(), (*id).into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,15 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::request::{
|
||||
MaybeInvalid,
|
||||
deserialize::{DeserializeArguments, deserialize_request},
|
||||
};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use types::{blob::BlobId, id::Id, type_state::DataType};
|
||||
use utils::map::vec_map::VecMap;
|
||||
|
||||
use crate::request::MaybeInvalid;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct BlobLookupRequest {
|
||||
pub account_id: Id,
|
||||
pub type_names: Vec<MaybeInvalid<DataType>>,
|
||||
@@ -35,38 +38,35 @@ pub struct BlobInfo {
|
||||
pub matched_ids: VecMap<DataType, Vec<Id>>,
|
||||
}
|
||||
|
||||
impl JsonObjectParser for BlobLookupRequest {
|
||||
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
|
||||
impl<'de> DeserializeArguments<'de> for BlobLookupRequest {
|
||||
fn deserialize_argument<A>(&mut self, key: &str, map: &mut A) -> Result<(), A::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
A: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut request = BlobLookupRequest {
|
||||
account_id: Id::default(),
|
||||
type_names: Vec::new(),
|
||||
ids: Vec::new(),
|
||||
};
|
||||
|
||||
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 if !key.is_ref => {
|
||||
request.account_id = parser.next_token::<Id>()?.unwrap_string("accountId")?;
|
||||
}
|
||||
0x0073_656d_614e_6570_7974 if !key.is_ref => {
|
||||
request.type_names = <Vec<MaybeInvalid<DataType>>>::parse(parser)?;
|
||||
}
|
||||
0x0073_6469 if !key.is_ref => {
|
||||
request.ids = <Vec<MaybeInvalid<BlobId>>>::parse(parser)?;
|
||||
}
|
||||
_ => {
|
||||
parser.skip_token(parser.depth_array, parser.depth_dict)?;
|
||||
}
|
||||
hashify::fnc_map!(key.as_bytes(),
|
||||
b"accountId" => {
|
||||
self.account_id = map.next_value()?;
|
||||
},
|
||||
b"typeNames" => {
|
||||
self.type_names = map.next_value()?;
|
||||
},
|
||||
b"ids" => {
|
||||
self.ids = map.next_value()?;
|
||||
},
|
||||
_ => {
|
||||
let _ = map.next_value::<serde::de::IgnoredAny>()?;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Ok(request)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for BlobLookupRequest {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,6 @@ pub mod upload;
|
||||
pub mod validate;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn ahash_is_empty<K, V>(map: &AHashMap<K, V>) -> bool {
|
||||
fn ahash_is_empty<K, V>(map: &AHashMap<K, V>) -> bool {
|
||||
map.is_empty()
|
||||
}
|
||||
|
||||
@@ -4,21 +4,24 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
object::email::{EmailProperty, EmailValue},
|
||||
request::{
|
||||
MaybeInvalid,
|
||||
deserialize::{DeserializeArguments, deserialize_request},
|
||||
},
|
||||
};
|
||||
use jmap_tools::Value;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use types::{blob::BlobId, id::Id};
|
||||
use utils::map::vec_map::VecMap;
|
||||
|
||||
use crate::{
|
||||
object::email::{EmailProperty, EmailValue},
|
||||
request::MaybeInvalid,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ParseEmailRequest {
|
||||
pub account_id: Id,
|
||||
pub blob_ids: Vec<MaybeInvalid<BlobId>>,
|
||||
pub properties: Option<Vec<EmailProperty>>,
|
||||
pub body_properties: Option<Vec<EmailProperty>>,
|
||||
pub properties: Option<Vec<MaybeInvalid<EmailProperty>>>,
|
||||
pub body_properties: Option<Vec<MaybeInvalid<EmailProperty>>>,
|
||||
pub fetch_text_body_values: Option<bool>,
|
||||
pub fetch_html_body_values: Option<bool>,
|
||||
pub fetch_all_body_values: Option<bool>,
|
||||
@@ -43,66 +46,50 @@ pub struct ParseEmailResponse {
|
||||
pub not_found: Vec<MaybeInvalid<BlobId>>,
|
||||
}
|
||||
|
||||
impl JsonObjectParser for ParseEmailRequest {
|
||||
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
|
||||
impl<'de> DeserializeArguments<'de> for ParseEmailRequest {
|
||||
fn deserialize_argument<A>(&mut self, key: &str, map: &mut A) -> Result<(), A::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
A: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut request = ParseEmailRequest {
|
||||
account_id: Id::default(),
|
||||
properties: None,
|
||||
blob_ids: vec![],
|
||||
body_properties: None,
|
||||
fetch_text_body_values: None,
|
||||
fetch_html_body_values: None,
|
||||
fetch_all_body_values: None,
|
||||
max_body_value_bytes: None,
|
||||
};
|
||||
|
||||
parser
|
||||
.next_token::<String>()?
|
||||
.assert_jmap(Token::DictStart)?;
|
||||
|
||||
while let Some(key) = parser.next_dict_key::<RequestProperty>()? {
|
||||
match (&key.hash[0], &key.hash[1]) {
|
||||
(0x0064_4974_6e75_6f63_6361, _) if !key.is_ref => {
|
||||
request.account_id = parser.next_token::<Id>()?.unwrap_string("accountId")?;
|
||||
}
|
||||
(0x0073_6449_626f_6c62, _) => {
|
||||
request.blob_ids = <Vec<BlobId>>::parse(parser)?;
|
||||
}
|
||||
(0x7365_6974_7265_706f_7270, _) => {
|
||||
request.properties = <Option<Vec<Property>>>::parse(parser)?;
|
||||
}
|
||||
(0x7365_6974_7265_706f_7250_7964_6f62, _) => {
|
||||
request.body_properties = <Option<Vec<Property>>>::parse(parser)?;
|
||||
}
|
||||
(0x6c61_5679_646f_4274_7865_5468_6374_6566, 0x0073_6575) => {
|
||||
request.fetch_text_body_values = parser
|
||||
.next_token::<Ignore>()?
|
||||
.unwrap_bool_or_null("fetchTextBodyValues")?;
|
||||
}
|
||||
(0x6c61_5679_646f_424c_4d54_4868_6374_6566, 0x0073_6575) => {
|
||||
request.fetch_html_body_values = parser
|
||||
.next_token::<Ignore>()?
|
||||
.unwrap_bool_or_null("fetchHTMLBodyValues")?;
|
||||
}
|
||||
(0x756c_6156_7964_6f42_6c6c_4168_6374_6566, 0x7365) => {
|
||||
request.fetch_all_body_values = parser
|
||||
.next_token::<Ignore>()?
|
||||
.unwrap_bool_or_null("fetchAllBodyValues")?;
|
||||
}
|
||||
(0x6574_7942_6575_6c61_5679_646f_4278_616d, 0x73) => {
|
||||
request.max_body_value_bytes = parser
|
||||
.next_token::<Ignore>()?
|
||||
.unwrap_usize_or_null("maxBodyValueBytes")?;
|
||||
}
|
||||
_ => {
|
||||
parser.skip_token(parser.depth_array, parser.depth_dict)?;
|
||||
}
|
||||
hashify::fnc_map!(key.as_bytes(),
|
||||
b"accountId" => {
|
||||
self.account_id = map.next_value()?;
|
||||
},
|
||||
b"blobIds" => {
|
||||
self.blob_ids = map.next_value()?;
|
||||
},
|
||||
b"properties" => {
|
||||
self.properties = map.next_value()?;
|
||||
},
|
||||
b"bodyProperties" => {
|
||||
self.body_properties = map.next_value()?;
|
||||
},
|
||||
b"fetchTextBodyValues" => {
|
||||
self.fetch_text_body_values = map.next_value()?;
|
||||
},
|
||||
b"fetchHTMLBodyValues" => {
|
||||
self.fetch_html_body_values = map.next_value()?;
|
||||
},
|
||||
b"fetchAllBodyValues" => {
|
||||
self.fetch_all_body_values = map.next_value()?;
|
||||
},
|
||||
b"maxBodyValueBytes" => {
|
||||
self.max_body_value_bytes = map.next_value()?;
|
||||
},
|
||||
_ => {
|
||||
let _ = map.next_value::<serde::de::IgnoredAny>()?;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Ok(request)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ParseEmailRequest {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,14 @@
|
||||
|
||||
use crate::{
|
||||
object::{JmapObject, email, mailbox},
|
||||
request::method::MethodObject,
|
||||
request::{
|
||||
deserialize::{DeserializeArguments, deserialize_request},
|
||||
method::MethodObject,
|
||||
},
|
||||
types::{date::UTCDate, state::State},
|
||||
};
|
||||
use compact_str::format_compact;
|
||||
use serde::{Deserialize, Deserializer, de::DeserializeOwned};
|
||||
use std::fmt::Display;
|
||||
use store::fts::{FilterItem, FilterType, FtsFilter};
|
||||
use types::{id::Id, keyword::Keyword};
|
||||
@@ -17,8 +21,8 @@ use types::{id::Id, keyword::Keyword};
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct QueryRequest<T: JmapObject> {
|
||||
pub account_id: Id,
|
||||
pub filter: Vec<T::Filter>,
|
||||
pub sort: Option<Vec<T::Comparator>>,
|
||||
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>,
|
||||
@@ -53,9 +57,19 @@ pub struct QueryResponse {
|
||||
pub limit: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Filter {
|
||||
Email(String),
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub enum Filter<T> {
|
||||
Property(T),
|
||||
|
||||
And,
|
||||
Or,
|
||||
Not,
|
||||
Close,
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Email(String),
|
||||
Name(String),
|
||||
DomainName(String),
|
||||
Text(String),
|
||||
@@ -100,18 +114,14 @@ pub enum Filter {
|
||||
ResourceType(String),
|
||||
_T(String),
|
||||
|
||||
And,
|
||||
Or,
|
||||
Not,
|
||||
Close,
|
||||
}
|
||||
*/
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Comparator {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
pub struct Comparator<T> {
|
||||
pub is_ascending: bool,
|
||||
pub collation: Option<String>,
|
||||
pub property: SortProperty,
|
||||
pub keyword: Option<Keyword>,
|
||||
pub property: T,
|
||||
//pub keyword: Option<Keyword>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -138,35 +148,58 @@ pub enum SortProperty {
|
||||
_T(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RequestArguments {
|
||||
Email(email::QueryArguments),
|
||||
Mailbox(mailbox::QueryArguments),
|
||||
EmailSubmission,
|
||||
SieveScript,
|
||||
Principal,
|
||||
Quota,
|
||||
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()?;
|
||||
},
|
||||
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 JsonObjectParser for QueryRequest<RequestArguments> {
|
||||
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
|
||||
impl<'de, T: JmapObject> Deserialize<'de> for QueryRequest<T> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let mut request = QueryRequest {
|
||||
arguments: match &parser.ctx {
|
||||
MethodObject::Email => RequestArguments::Email(Default::default()),
|
||||
MethodObject::Mailbox => RequestArguments::Mailbox(Default::default()),
|
||||
MethodObject::EmailSubmission => RequestArguments::EmailSubmission,
|
||||
MethodObject::SieveScript => RequestArguments::SieveScript,
|
||||
MethodObject::Principal => RequestArguments::Principal,
|
||||
MethodObject::Quota => RequestArguments::Quota,
|
||||
_ => {
|
||||
return Err(trc::JmapEvent::UnknownMethod
|
||||
.into_err()
|
||||
.details(format_compact!("{}/query", parser.ctx)));
|
||||
}
|
||||
},
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: JmapObject> Default for QueryRequest<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
account_id: Id::default(),
|
||||
filter: vec![],
|
||||
sort: None,
|
||||
position: None,
|
||||
@@ -174,72 +207,12 @@ impl JsonObjectParser for QueryRequest<RequestArguments> {
|
||||
anchor_offset: None,
|
||||
limit: None,
|
||||
calculate_total: None,
|
||||
account_id: Id::default(),
|
||||
};
|
||||
|
||||
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"));
|
||||
}
|
||||
},
|
||||
0x6e6f_6974_6973_6f70 => {
|
||||
request.position = parser
|
||||
.next_token::<Ignore>()?
|
||||
.unwrap_ints_or_null("position")?;
|
||||
}
|
||||
0x726f_6863_6e61 => {
|
||||
request.anchor = parser.next_token::<Id>()?.unwrap_string_or_null("anchor")?;
|
||||
}
|
||||
0x7465_7366_664f_726f_6863_6e61 => {
|
||||
request.anchor_offset = parser
|
||||
.next_token::<Ignore>()?
|
||||
.unwrap_ints_or_null("anchorOffset")?;
|
||||
}
|
||||
0x0074_696d_696c => {
|
||||
request.limit = parser
|
||||
.next_token::<Ignore>()?
|
||||
.unwrap_usize_or_null("limit")?;
|
||||
}
|
||||
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)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
arguments: T::QueryArguments::default(),
|
||||
}
|
||||
|
||||
Ok(request)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn parse_filter(parser: &mut Parser) -> trc::Result<Vec<Filter>> {
|
||||
let mut filter = vec![Filter::Close];
|
||||
let mut pos_stack = vec![0];
|
||||
@@ -581,6 +554,8 @@ impl JsonObjectParser for SortProperty {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl Display for Filter {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
@@ -720,28 +695,6 @@ impl Comparator {
|
||||
}
|
||||
}
|
||||
|
||||
impl QueryRequest<RequestArguments> {
|
||||
pub fn take_arguments(&mut self) -> RequestArguments {
|
||||
std::mem::replace(&mut self.arguments, RequestArguments::SieveScript)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> QueryRequest<T> {
|
||||
pub fn with_arguments<A>(self, arguments: A) -> QueryRequest<A> {
|
||||
QueryRequest {
|
||||
arguments,
|
||||
account_id: self.account_id,
|
||||
filter: self.filter,
|
||||
sort: self.sort,
|
||||
position: self.position,
|
||||
anchor: self.anchor,
|
||||
anchor_offset: self.anchor_offset,
|
||||
limit: self.limit,
|
||||
calculate_total: self.calculate_total,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Filter> for store::query::Filter {
|
||||
fn from(value: Filter) -> Self {
|
||||
match value {
|
||||
@@ -797,3 +750,5 @@ impl From<FilterType> for Filter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@@ -4,9 +4,12 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::query::{Comparator, Filter, RequestArguments, parse_filter, parse_sort};
|
||||
use crate::{object::JmapObject, request::method::MethodObject, types::state::State};
|
||||
use compact_str::format_compact;
|
||||
use crate::{
|
||||
object::JmapObject,
|
||||
request::deserialize::{DeserializeArguments, deserialize_request},
|
||||
types::state::State,
|
||||
};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use types::id::Id;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -55,87 +58,62 @@ impl AddedItem {
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonObjectParser for QueryChangesRequest {
|
||||
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
|
||||
impl<'de, T: JmapObject> DeserializeArguments<'de> for QueryChangesRequest<T> {
|
||||
fn deserialize_argument<A>(&mut self, key: &str, map: &mut A) -> Result<(), A::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
A: serde::de::MapAccess<'de>,
|
||||
{
|
||||
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)));
|
||||
}
|
||||
hashify::fnc_map!(key.as_bytes(),
|
||||
b"accountId" => {
|
||||
self.account_id = map.next_value()?;
|
||||
},
|
||||
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)?;
|
||||
}
|
||||
}
|
||||
b"filter" => {
|
||||
self.filter = map.next_value()?;
|
||||
},
|
||||
b"sort" => {
|
||||
self.sort = map.next_value()?;
|
||||
},
|
||||
b"sinceQueryState" => {
|
||||
self.since_query_state = map.next_value()?;
|
||||
},
|
||||
b"maxChanges" => {
|
||||
self.max_changes = map.next_value()?;
|
||||
},
|
||||
b"upToId" => {
|
||||
self.up_to_id = map.next_value()?;
|
||||
},
|
||||
b"calculateTotal" => {
|
||||
self.calculate_total = map.next_value()?;
|
||||
},
|
||||
_ => {
|
||||
self.arguments.deserialize_argument(key, map)?;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Ok(request)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T: JmapObject> Deserialize<'de> for QueryChangesRequest<T> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: JmapObject> Default for QueryChangesRequest<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
account_id: Id::default(),
|
||||
filter: Vec::new(),
|
||||
sort: None,
|
||||
since_query_state: State::default(),
|
||||
max_changes: None,
|
||||
up_to_id: None,
|
||||
calculate_total: None,
|
||||
arguments: T::QueryArguments::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,20 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::query::{Filter, parse_filter};
|
||||
use crate::request::reference::{MaybeResultReference, ResultReference};
|
||||
use super::query::Filter;
|
||||
use crate::request::{
|
||||
MaybeInvalid,
|
||||
deserialize::{DeserializeArguments, deserialize_request},
|
||||
reference::{MaybeResultReference, ResultReference},
|
||||
};
|
||||
use serde::{Deserialize, Deserializer, de::DeserializeOwned};
|
||||
use types::id::Id;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GetSearchSnippetRequest {
|
||||
pub struct GetSearchSnippetRequest<T> {
|
||||
pub account_id: Id,
|
||||
pub filter: Vec<Filter>,
|
||||
pub email_ids: MaybeResultReference<Vec<Id>>,
|
||||
pub filter: Vec<Filter<T>>,
|
||||
pub email_ids: MaybeResultReference<Vec<MaybeInvalid<Id>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
@@ -25,7 +30,7 @@ pub struct GetSearchSnippetResponse {
|
||||
|
||||
#[serde(rename = "notFound")]
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub not_found: Vec<Id>,
|
||||
pub not_found: Vec<MaybeInvalid<Id>>,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, Clone, Debug)]
|
||||
@@ -40,48 +45,48 @@ pub struct SearchSnippet {
|
||||
pub preview: Option<String>,
|
||||
}
|
||||
|
||||
impl JsonObjectParser for GetSearchSnippetRequest {
|
||||
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
|
||||
impl<'de, T: DeserializeOwned> DeserializeArguments<'de> for GetSearchSnippetRequest<T> {
|
||||
fn deserialize_argument<A>(&mut self, key: &str, map: &mut A) -> Result<(), A::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
A: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut request = GetSearchSnippetRequest {
|
||||
account_id: Id::default(),
|
||||
filter: vec![],
|
||||
email_ids: MaybeReference::Value(vec![]),
|
||||
};
|
||||
|
||||
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 if !key.is_ref => {
|
||||
request.account_id = parser.next_token::<Id>()?.unwrap_string("accountId")?;
|
||||
}
|
||||
0x7265_746c_6966 if !key.is_ref => match parser.next_token::<Ignore>()? {
|
||||
Token::DictStart => {
|
||||
request.filter = parse_filter(parser)?;
|
||||
}
|
||||
Token::Null => (),
|
||||
token => {
|
||||
return Err(token.error("filter", "object or null"));
|
||||
}
|
||||
},
|
||||
0x7364_496c_6961_6d65 => {
|
||||
request.email_ids = if !key.is_ref {
|
||||
MaybeReference::Value(<Vec<Id>>::parse(parser)?)
|
||||
} else {
|
||||
MaybeReference::Reference(ResultReference::parse(parser)?)
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
parser.skip_token(parser.depth_array, parser.depth_dict)?;
|
||||
}
|
||||
hashify::fnc_map!(key.as_bytes(),
|
||||
b"accountId" => {
|
||||
self.account_id = map.next_value()?;
|
||||
},
|
||||
b"filter" => {
|
||||
self.filter = map.next_value()?;
|
||||
},
|
||||
b"emailIds" => {
|
||||
self.email_ids = MaybeResultReference::Value(map.next_value::<Vec<MaybeInvalid<Id>>>()?);
|
||||
},
|
||||
b"#emailIds" => {
|
||||
self.email_ids = MaybeResultReference::Reference(map.next_value::<ResultReference>()?);
|
||||
},
|
||||
_ => {
|
||||
let _ = map.next_value::<serde::de::IgnoredAny>()?;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Ok(request)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T: DeserializeOwned> Deserialize<'de> for GetSearchSnippetRequest<T> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for GetSearchSnippetRequest<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
account_id: Id::default(),
|
||||
filter: Vec::new(),
|
||||
email_ids: MaybeResultReference::Value(Vec::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::{
|
||||
object::JmapObject,
|
||||
request::{
|
||||
MaybeInvalid,
|
||||
deserialize::{DeserializeArguments, deserialize_request},
|
||||
method::MethodObject,
|
||||
reference::{MaybeResultReference, ResultReference},
|
||||
},
|
||||
@@ -19,10 +20,12 @@ use crate::{
|
||||
use ahash::AHashMap;
|
||||
use compact_str::format_compact;
|
||||
use jmap_tools::Value;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use types::{acl::Acl, blob::BlobId, id::Id, keyword::Keyword};
|
||||
use utils::map::{bitmap::Bitmap, vec_map::VecMap};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub struct SetRequest<'x, T: JmapObject> {
|
||||
pub account_id: Id,
|
||||
pub if_in_state: Option<State>,
|
||||
@@ -32,17 +35,6 @@ pub struct SetRequest<'x, T: JmapObject> {
|
||||
pub arguments: T::SetArguments,
|
||||
}
|
||||
|
||||
/*#[derive(Debug, Clone)]
|
||||
pub enum RequestArguments {
|
||||
Email,
|
||||
Mailbox(mailbox::SetArguments),
|
||||
Identity,
|
||||
EmailSubmission(email_submission::SetArguments),
|
||||
PushSubscription,
|
||||
SieveScript(sieve::SetArguments),
|
||||
VacationResponse,
|
||||
}*/
|
||||
|
||||
#[derive(Debug, Clone, Default, serde::Serialize)]
|
||||
pub struct SetResponse<T: JmapObject> {
|
||||
#[serde(rename = "accountId")]
|
||||
@@ -82,306 +74,67 @@ pub struct SetResponse<T: JmapObject> {
|
||||
pub not_destroyed: VecMap<MaybeInvalid<Id>, SetError<T::Property>>,
|
||||
}
|
||||
|
||||
impl JsonObjectParser for SetRequest<RequestArguments> {
|
||||
fn parse(parser: &mut Parser) -> trc::Result<Self>
|
||||
impl<'de, T: JmapObject> DeserializeArguments<'de> for SetRequest<'de, T> {
|
||||
fn deserialize_argument<A>(&mut self, key: &str, map: &mut A) -> Result<(), A::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
A: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut request = SetRequest {
|
||||
arguments: match &parser.ctx {
|
||||
MethodObject::Email => RequestArguments::Email,
|
||||
MethodObject::Mailbox => RequestArguments::Mailbox(Default::default()),
|
||||
MethodObject::Identity => RequestArguments::Identity,
|
||||
MethodObject::EmailSubmission => {
|
||||
RequestArguments::EmailSubmission(Default::default())
|
||||
}
|
||||
MethodObject::PushSubscription => RequestArguments::PushSubscription,
|
||||
MethodObject::VacationResponse => RequestArguments::VacationResponse,
|
||||
MethodObject::SieveScript => RequestArguments::SieveScript(Default::default()),
|
||||
_ => {
|
||||
return Err(trc::JmapEvent::UnknownMethod
|
||||
.into_err()
|
||||
.details(format_compact!("{}/set", parser.ctx)));
|
||||
}
|
||||
hashify::fnc_map!(key.as_bytes(),
|
||||
b"accountId" => {
|
||||
self.account_id = map.next_value()?;
|
||||
},
|
||||
b"ifInState" => {
|
||||
self.if_in_state = map.next_value()?;
|
||||
},
|
||||
b"create" => {
|
||||
self.create = map.next_value()?;
|
||||
},
|
||||
b"update" => {
|
||||
self.update = map.next_value()?;
|
||||
},
|
||||
b"destroy" => {
|
||||
self.destroy = map.next_value::<Option<Vec<MaybeInvalid<Id>>>>()?.map(MaybeResultReference::Value);
|
||||
},
|
||||
b"#destroy" => {
|
||||
self.destroy = Some(MaybeResultReference::Reference(map.next_value::<ResultReference>()?));
|
||||
}
|
||||
_ => {
|
||||
self.arguments.deserialize_argument(key, map)?;
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T: JmapObject> Deserialize<'de> for SetRequest<'de, T> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'x, T: JmapObject> Default for SetRequest<'x, T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
account_id: Id::default(),
|
||||
if_in_state: None,
|
||||
create: None,
|
||||
update: None,
|
||||
destroy: 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 if !key.is_ref => {
|
||||
request.account_id = parser.next_token::<Id>()?.unwrap_string("accountId")?;
|
||||
}
|
||||
0x6574_6165_7263 if !key.is_ref => {
|
||||
request.create = <Option<VecMap<String, Value<'x, P, E>>>>::parse(parser)?;
|
||||
}
|
||||
0x6574_6164_7075 if !key.is_ref => {
|
||||
request.update = <Option<VecMap<Id, Value<'x, P, E>>>>::parse(parser)?;
|
||||
}
|
||||
0x0079_6f72_7473_6564 => {
|
||||
request.destroy = if !key.is_ref {
|
||||
<Option<Vec<Id>>>::parse(parser)?.map(MaybeReference::Value)
|
||||
} else {
|
||||
Some(MaybeReference::Reference(ResultReference::parse(parser)?))
|
||||
};
|
||||
}
|
||||
0x0065_7461_7453_6e49_6669 if !key.is_ref => {
|
||||
request.if_in_state = parser
|
||||
.next_token::<State>()?
|
||||
.unwrap_string_or_null("ifInState")?;
|
||||
}
|
||||
_ => {
|
||||
if !request.arguments.parse(parser, key)? {
|
||||
parser.skip_token(parser.depth_array, parser.depth_dict)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonObjectParser for Value<'x, P, E> {
|
||||
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let mut obj = Object(VecMap::with_capacity(8));
|
||||
|
||||
parser
|
||||
.next_token::<String>()?
|
||||
.assert_jmap(Token::DictStart)?;
|
||||
|
||||
while let Some(mut key) = parser.next_dict_key::<SetProperty>()? {
|
||||
let value = if !key.is_ref {
|
||||
match &key.property {
|
||||
Property::Id | Property::ThreadId => parser
|
||||
.next_token::<Id>()?
|
||||
.unwrap_string_or_null("")?
|
||||
.map(|id| SetValue::Value(Value::Id(id)))
|
||||
.unwrap_or(SetValue::Value(Value::Null)),
|
||||
Property::BlobId | Property::Picture => parser
|
||||
.next_token::<MaybeReference<BlobId, String>>()?
|
||||
.unwrap_string_or_null("")?
|
||||
.map(SetValue::from)
|
||||
.unwrap_or(SetValue::Value(Value::Null)),
|
||||
Property::SentAt
|
||||
| Property::ReceivedAt
|
||||
| Property::Expires
|
||||
| Property::FromDate
|
||||
| Property::ToDate => parser
|
||||
.next_token::<UTCDate>()?
|
||||
.unwrap_string_or_null("")?
|
||||
.map(|date| SetValue::Value(Value::Date(date)))
|
||||
.unwrap_or(SetValue::Value(Value::Null)),
|
||||
Property::Subject
|
||||
| Property::Preview
|
||||
| Property::Name
|
||||
| Property::Description
|
||||
| Property::Timezone
|
||||
| Property::Email
|
||||
| Property::Secret
|
||||
| Property::DeviceClientId
|
||||
| Property::Url
|
||||
| Property::VerificationCode
|
||||
| Property::HtmlSignature
|
||||
| Property::TextSignature
|
||||
| Property::Type
|
||||
| Property::Charset
|
||||
| Property::Disposition
|
||||
| Property::Language
|
||||
| Property::Location
|
||||
| Property::Cid
|
||||
| Property::Role
|
||||
| Property::PartId => parser
|
||||
.next_token::<String>()?
|
||||
.unwrap_string_or_null("")?
|
||||
.map(|text| SetValue::Value(Value::Text(text)))
|
||||
.unwrap_or(SetValue::Value(Value::Null)),
|
||||
Property::TextBody | Property::HtmlBody => {
|
||||
if let MethodObject::Email = &parser.ctx {
|
||||
SetValue::Value(Value::parse::<ObjectProperty, String>(
|
||||
parser.next_token()?,
|
||||
parser,
|
||||
)?)
|
||||
} else {
|
||||
parser
|
||||
.next_token::<String>()?
|
||||
.unwrap_string_or_null("")?
|
||||
.map(|text| SetValue::Value(Value::Text(text)))
|
||||
.unwrap_or(SetValue::Value(Value::Null))
|
||||
}
|
||||
}
|
||||
Property::HasAttachment
|
||||
| Property::IsSubscribed
|
||||
| Property::IsEnabled
|
||||
| Property::IsActive => parser
|
||||
.next_token::<String>()?
|
||||
.unwrap_bool_or_null("")?
|
||||
.map(|bool| SetValue::Value(Value::Bool(bool)))
|
||||
.unwrap_or(SetValue::Value(Value::Null)),
|
||||
Property::Size | Property::SortOrder | Property::Quota => parser
|
||||
.next_token::<String>()?
|
||||
.unwrap_uint_or_null("")?
|
||||
.map(|uint| SetValue::Value(Value::UnsignedInt(uint)))
|
||||
.unwrap_or(SetValue::Value(Value::Null)),
|
||||
Property::ParentId | Property::EmailId | Property::IdentityId => parser
|
||||
.next_token::<MaybeReference<Id, String>>()?
|
||||
.unwrap_string_or_null("")?
|
||||
.map(SetValue::from)
|
||||
.unwrap_or(SetValue::Value(Value::Null)),
|
||||
Property::MailboxIds => {
|
||||
if key.patch.is_empty() {
|
||||
SetValue::from(
|
||||
<SetValueMap<MaybeReference<Id, String>>>::parse(parser)?.values,
|
||||
)
|
||||
} else {
|
||||
key.patch.push(Value::Bool(bool::parse(parser)?));
|
||||
SetValue::Patch(key.patch)
|
||||
}
|
||||
}
|
||||
Property::Keywords => {
|
||||
if key.patch.is_empty() {
|
||||
SetValue::Value(Value::List(
|
||||
<SetValueMap<Keyword>>::parse(parser)?
|
||||
.values
|
||||
.into_iter()
|
||||
.map(Value::Keyword)
|
||||
.collect(),
|
||||
))
|
||||
} else {
|
||||
key.patch.push(Value::Bool(bool::parse(parser)?));
|
||||
SetValue::Patch(key.patch)
|
||||
}
|
||||
}
|
||||
|
||||
Property::Acl => match key.patch.len() {
|
||||
0 => {
|
||||
parser
|
||||
.next_token::<String>()?
|
||||
.assert_jmap(Token::DictStart)?;
|
||||
let mut acls = Vec::new();
|
||||
while let Some(account) = parser.next_dict_key::<String>()? {
|
||||
acls.push(Value::Text(account));
|
||||
acls.push(Value::UnsignedInt(<Bitmap<Acl>>::parse(parser)?.into()));
|
||||
}
|
||||
SetValue::Value(Value::List(acls))
|
||||
}
|
||||
1 => {
|
||||
key.patch
|
||||
.push(Value::UnsignedInt(<Bitmap<Acl>>::parse(parser)?.into()));
|
||||
SetValue::Patch(key.patch)
|
||||
}
|
||||
2 => {
|
||||
key.patch.push(Value::Bool(bool::parse(parser)?));
|
||||
SetValue::Patch(key.patch)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Property::Aliases
|
||||
| Property::Attachments
|
||||
| Property::Bcc
|
||||
| Property::BodyStructure
|
||||
| Property::BodyValues
|
||||
| Property::Capabilities
|
||||
| Property::Cc
|
||||
| Property::Envelope
|
||||
| Property::From
|
||||
| Property::Headers
|
||||
| Property::InReplyTo
|
||||
| Property::Keys
|
||||
| Property::MessageId
|
||||
| Property::References
|
||||
| Property::ReplyTo
|
||||
| Property::Sender
|
||||
| Property::SubParts
|
||||
| Property::To
|
||||
| Property::UndoStatus
|
||||
| Property::Types => SetValue::Value(Value::parse::<ObjectProperty, String>(
|
||||
parser.next_token()?,
|
||||
parser,
|
||||
)?),
|
||||
Property::Parameters => SetValue::Value(Value::parse::<String, String>(
|
||||
parser.next_token()?,
|
||||
parser,
|
||||
)?),
|
||||
Property::Members => SetValue::Value(Value::parse::<ObjectProperty, Id>(
|
||||
parser.next_token()?,
|
||||
parser,
|
||||
)?),
|
||||
Property::Header(h) => SetValue::Value(if matches!(h.form, HeaderForm::Date) {
|
||||
Value::parse::<ObjectProperty, UTCDate>(parser.next_token()?, parser)
|
||||
} else {
|
||||
Value::parse::<ObjectProperty, String>(parser.next_token()?, parser)
|
||||
}?),
|
||||
|
||||
_ => {
|
||||
parser.skip_token(parser.depth_array, parser.depth_dict)?;
|
||||
SetValue::Value(Value::Null)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SetValue::ResultReference(ResultReference::parse(parser)?)
|
||||
};
|
||||
|
||||
obj.0.append(key.property, value);
|
||||
}
|
||||
|
||||
Ok(obj)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<AnyId>> From<MaybeReference<T, String>> for SetValue {
|
||||
fn from(reference: MaybeReference<T, String>) -> Self {
|
||||
match reference {
|
||||
MaybeReference::Value(id) => SetValue::IdReference(MaybeReference::Value(id.into())),
|
||||
MaybeReference::Reference(reference) => {
|
||||
SetValue::IdReference(MaybeReference::Reference(reference))
|
||||
}
|
||||
arguments: T::SetArguments::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<AnyId>> From<Vec<MaybeReference<T, String>>> for SetValue {
|
||||
fn from(value: Vec<MaybeReference<T, String>>) -> Self {
|
||||
SetValue::IdReferences(
|
||||
value
|
||||
.into_iter()
|
||||
.map(|reference| match reference {
|
||||
MaybeReference::Value(id) => MaybeReference::Value(id.into()),
|
||||
MaybeReference::Reference(reference) => MaybeReference::Reference(reference),
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl RequestPropertyParser for RequestArguments {
|
||||
fn parse(&mut self, parser: &mut Parser, property: RequestProperty) -> trc::Result<bool> {
|
||||
match self {
|
||||
RequestArguments::Mailbox(args) => args.parse(parser, property),
|
||||
RequestArguments::EmailSubmission(args) => args.parse(parser, property),
|
||||
RequestArguments::SieveScript(args) => args.parse(parser, property),
|
||||
_ => Ok(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SetRequest<T> {
|
||||
impl<'x, T: JmapObject> SetRequest<'x, T> {
|
||||
pub fn validate(&self, max_objects_in_set: usize) -> trc::Result<()> {
|
||||
if self.create.as_ref().map_or(0, |objs| objs.len())
|
||||
+ self.update.as_ref().map_or(0, |objs| objs.len())
|
||||
+ self.destroy.as_ref().map_or(0, |objs| {
|
||||
if let MaybeReference::Value(ids) = objs {
|
||||
if let MaybeResultReference::Value(ids) = objs {
|
||||
ids.len()
|
||||
} else {
|
||||
0
|
||||
@@ -403,45 +156,31 @@ impl<T> SetRequest<T> {
|
||||
self.create.as_ref().is_some_and(|objs| !objs.is_empty())
|
||||
}
|
||||
|
||||
pub fn unwrap_create(&mut self) -> VecMap<String, Value<'x, P, E>> {
|
||||
pub fn unwrap_create(&mut self) -> VecMap<String, Value<'x, T::Property, T::Element>> {
|
||||
self.create.take().unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn unwrap_update(&mut self) -> VecMap<Id, Value<'x, P, E>> {
|
||||
pub fn unwrap_update(
|
||||
&mut self,
|
||||
) -> VecMap<MaybeInvalid<Id>, Value<'x, T::Property, T::Element>> {
|
||||
self.update.take().unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn unwrap_destroy(&mut self) -> Vec<Id> {
|
||||
/*pub fn unwrap_destroy(&mut self) -> Vec<Id> {
|
||||
self.destroy
|
||||
.take()
|
||||
.map(|ids| ids.unwrap())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
impl SetRequest<RequestArguments> {
|
||||
pub fn take_arguments(&mut self) -> RequestArguments {
|
||||
std::mem::replace(&mut self.arguments, RequestArguments::VacationResponse)
|
||||
}
|
||||
|
||||
pub fn with_arguments<T>(self, arguments: T) -> SetRequest<T> {
|
||||
SetRequest {
|
||||
account_id: self.account_id,
|
||||
if_in_state: self.if_in_state,
|
||||
create: self.create,
|
||||
update: self.update,
|
||||
destroy: self.destroy,
|
||||
arguments,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SetResponse {
|
||||
pub fn from_request<T>(request: &SetRequest<T>, max_objects: usize) -> trc::Result<Self> {
|
||||
/*
|
||||
impl<T: JmapObject> SetResponse<T> {
|
||||
pub fn from_request(request: &SetRequest<T>, max_objects: usize) -> trc::Result<Self> {
|
||||
let n_create = request.create.as_ref().map_or(0, |objs| objs.len());
|
||||
let n_update = request.update.as_ref().map_or(0, |objs| objs.len());
|
||||
let n_destroy = request.destroy.as_ref().map_or(0, |objs| {
|
||||
if let MaybeReference::Value(ids) = objs {
|
||||
if let MaybeResultReference::Value(ids) = objs {
|
||||
ids.len()
|
||||
} else {
|
||||
0
|
||||
@@ -527,3 +266,4 @@ impl SetResponse {
|
||||
!self.created.is_empty() || !self.updated.is_empty() || !self.destroyed.is_empty()
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -4,29 +4,37 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::ahash_is_empty;
|
||||
use crate::{
|
||||
error::set::SetError, object::blob::BlobProperty, request::reference::MaybeIdReference,
|
||||
error::set::SetError,
|
||||
object::blob::BlobProperty,
|
||||
request::{
|
||||
deserialize::{DeserializeArguments, deserialize_request},
|
||||
reference::MaybeIdReference,
|
||||
},
|
||||
response::Response,
|
||||
};
|
||||
use ahash::AHashMap;
|
||||
use mail_parser::decoders::base64::base64_decode;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use types::{blob::BlobId, id::Id};
|
||||
use utils::map::vec_map::VecMap;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct BlobUploadRequest {
|
||||
pub account_id: Id,
|
||||
pub create: VecMap<String, UploadObject>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct UploadObject {
|
||||
pub type_: Option<String>,
|
||||
pub data: Vec<DataSourceObject>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub enum DataSourceObject {
|
||||
Id {
|
||||
id: MaybeIdReference<BlobId>,
|
||||
@@ -34,6 +42,8 @@ pub enum DataSourceObject {
|
||||
offset: Option<usize>,
|
||||
},
|
||||
Value(Vec<u8>),
|
||||
#[default]
|
||||
Null,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, serde::Serialize)]
|
||||
@@ -59,148 +69,112 @@ pub struct BlobUploadResponseObject {
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
impl JsonObjectParser for BlobUploadRequest {
|
||||
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
|
||||
impl<'de> DeserializeArguments<'de> for BlobUploadRequest {
|
||||
fn deserialize_argument<A>(&mut self, key: &str, map: &mut A) -> Result<(), A::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
A: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut request = BlobUploadRequest {
|
||||
account_id: Id::default(),
|
||||
create: VecMap::new(),
|
||||
};
|
||||
|
||||
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 if !key.is_ref => {
|
||||
request.account_id = parser.next_token::<Id>()?.unwrap_string("accountId")?;
|
||||
}
|
||||
0x6574_6165_7263 if !key.is_ref => {
|
||||
request.create = <VecMap<String, UploadObject>>::parse(parser)?;
|
||||
}
|
||||
_ => {
|
||||
parser.skip_token(parser.depth_array, parser.depth_dict)?;
|
||||
}
|
||||
hashify::fnc_map!(key.as_bytes(),
|
||||
b"accountId" => {
|
||||
self.account_id = map.next_value()?;
|
||||
},
|
||||
b"create" => {
|
||||
self.create = map.next_value()?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let _ = map.next_value::<serde::de::IgnoredAny>()?;
|
||||
}
|
||||
);
|
||||
|
||||
Ok(request)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonObjectParser for UploadObject {
|
||||
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
|
||||
impl<'de> DeserializeArguments<'de> for UploadObject {
|
||||
fn deserialize_argument<A>(&mut self, key: &str, map: &mut A) -> Result<(), A::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
A: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut request = UploadObject {
|
||||
type_: None,
|
||||
data: Vec::new(),
|
||||
};
|
||||
hashify::fnc_map!(key.as_bytes(),
|
||||
b"type" => {
|
||||
self.type_ = map.next_value()?;
|
||||
},
|
||||
b"data" => {
|
||||
self.data = map.next_value()?;
|
||||
},
|
||||
_ => {
|
||||
let _ = map.next_value::<serde::de::IgnoredAny>()?;
|
||||
}
|
||||
);
|
||||
|
||||
parser
|
||||
.next_token::<String>()?
|
||||
.assert_jmap(Token::DictStart)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(key) = parser.next_dict_key::<RequestProperty>()? {
|
||||
match &key.hash[0] {
|
||||
0x6570_7974 if !key.is_ref => {
|
||||
request.type_ = parser
|
||||
.next_token::<String>()?
|
||||
.unwrap_string_or_null("type")?;
|
||||
}
|
||||
0x6174_6164 if !key.is_ref => {
|
||||
parser.next_token::<Ignore>()?.assert(Token::ArrayStart)?;
|
||||
loop {
|
||||
match parser.next_token::<Ignore>()? {
|
||||
Token::Comma => (),
|
||||
Token::ArrayEnd => break,
|
||||
Token::DictStart => {
|
||||
request.data.push(DataSourceObject::parse(parser)?);
|
||||
}
|
||||
token => return Err(token.error("", "DataSourceObject")),
|
||||
}
|
||||
impl<'de> DeserializeArguments<'de> for DataSourceObject {
|
||||
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"data:asText" => {
|
||||
*self = DataSourceObject::Value(map.next_value::<String>().map(|v| v.into_bytes())?);
|
||||
},
|
||||
b"data:asBase64" => {
|
||||
*self = DataSourceObject::Value(base64_decode(map.next_value::<Cow<'_, str>>()?.as_bytes()).ok_or_else(|| serde::de::Error::custom("Failed to decode base64 data"))?);
|
||||
},
|
||||
b"blobId" => {
|
||||
match self {
|
||||
DataSourceObject::Id { id, .. } => {
|
||||
*id = map.next_value()?;
|
||||
},
|
||||
_ => {
|
||||
*self = DataSourceObject::Id {
|
||||
id: map.next_value()?,
|
||||
length: None,
|
||||
offset: None,
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
parser.skip_token(parser.depth_array, parser.depth_dict)?;
|
||||
},
|
||||
b"offset" => {
|
||||
match self {
|
||||
DataSourceObject::Id { offset, .. } => {
|
||||
*offset = map.next_value()?;
|
||||
},
|
||||
_ => {
|
||||
*self = DataSourceObject::Id {
|
||||
id: MaybeIdReference::Invalid("".into()),
|
||||
length: None,
|
||||
offset: map.next_value()?,
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
b"length" => {
|
||||
match self {
|
||||
DataSourceObject::Id { length, .. } => {
|
||||
*length = map.next_value()?;
|
||||
},
|
||||
_ => {
|
||||
*self = DataSourceObject::Id {
|
||||
id: MaybeIdReference::Invalid("".into()),
|
||||
length: map.next_value()?,
|
||||
offset: None,
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
let _ = map.next_value::<serde::de::IgnoredAny>()?;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Ok(request)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonObjectParser for DataSourceObject {
|
||||
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let mut data: Option<Vec<u8>> = None;
|
||||
let mut blob_id: Option<MaybeReference<BlobId, String>> = None;
|
||||
let mut offset: Option<usize> = None;
|
||||
let mut length: Option<usize> = None;
|
||||
|
||||
while let Some(key) = parser.next_dict_key::<RequestProperty>()? {
|
||||
match &key.hash[0] {
|
||||
0x0074_7865_5473_613a_6174_6164 if !key.is_ref => {
|
||||
data = parser
|
||||
.next_token::<String>()?
|
||||
.unwrap_string("data:asText")?
|
||||
.into_bytes()
|
||||
.into();
|
||||
}
|
||||
0x0034_3665_7361_4273_613a_6174_6164 if !key.is_ref => {
|
||||
data = base64_decode(
|
||||
parser
|
||||
.next_token::<String>()?
|
||||
.unwrap_string("data:asBase64")?
|
||||
.as_bytes(),
|
||||
)
|
||||
.ok_or_else(|| parser.error("Failed to decode data:asBase64"))?
|
||||
.into();
|
||||
}
|
||||
0x6449_626f_6c62 if !key.is_ref => {
|
||||
blob_id = parser
|
||||
.next_token::<MaybeReference<BlobId, String>>()?
|
||||
.unwrap_string("blobId")?
|
||||
.into();
|
||||
}
|
||||
0x6874_676e_656c if !key.is_ref => {
|
||||
length = parser
|
||||
.next_token::<Ignore>()?
|
||||
.unwrap_usize_or_null("length")?;
|
||||
}
|
||||
0x7465_7366_666f if !key.is_ref => {
|
||||
offset = parser
|
||||
.next_token::<Ignore>()?
|
||||
.unwrap_usize_or_null("offset")?;
|
||||
}
|
||||
_ => {
|
||||
parser.skip_token(parser.depth_array, parser.depth_dict)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(data) = data {
|
||||
Ok(DataSourceObject::Value(data))
|
||||
} else if let Some(blob_id) = blob_id {
|
||||
Ok(DataSourceObject::Id {
|
||||
id: blob_id,
|
||||
length,
|
||||
offset,
|
||||
})
|
||||
} else {
|
||||
Err(parser.error("Missing data or blobId in DataSourceObject"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BlobUploadResponse {
|
||||
/*impl BlobUploadResponse {
|
||||
pub fn update_created_ids(&self, response: &mut Response) {
|
||||
for (user_id, obj) in &self.created {
|
||||
response
|
||||
@@ -208,4 +182,31 @@ impl BlobUploadResponse {
|
||||
.insert(user_id.clone(), obj.id.clone().into());
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
impl<'de> Deserialize<'de> for DataSourceObject {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for UploadObject {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for BlobUploadRequest {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,18 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{error::set::SetError, object::sieve::SieveProperty, request::MaybeInvalid};
|
||||
use serde::Serialize;
|
||||
use crate::{
|
||||
error::set::SetError,
|
||||
object::sieve::SieveProperty,
|
||||
request::{
|
||||
MaybeInvalid,
|
||||
deserialize::{DeserializeArguments, deserialize_request},
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use types::{blob::BlobId, id::Id};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ValidateSieveScriptRequest {
|
||||
pub account_id: Id,
|
||||
pub blob_id: MaybeInvalid<BlobId>,
|
||||
@@ -21,34 +28,32 @@ pub struct ValidateSieveScriptResponse {
|
||||
pub error: Option<SetError<SieveProperty>>,
|
||||
}
|
||||
|
||||
impl JsonObjectParser for ValidateSieveScriptRequest {
|
||||
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
|
||||
impl<'de> DeserializeArguments<'de> for ValidateSieveScriptRequest {
|
||||
fn deserialize_argument<A>(&mut self, key: &str, map: &mut A) -> Result<(), A::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
A: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut request = ValidateSieveScriptRequest {
|
||||
account_id: Id::default(),
|
||||
blob_id: BlobId::default(),
|
||||
};
|
||||
|
||||
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 if !key.is_ref => {
|
||||
request.account_id = parser.next_token::<Id>()?.unwrap_string("accountId")?;
|
||||
}
|
||||
0x6449_626f_6c62 if !key.is_ref => {
|
||||
request.blob_id = parser.next_token::<BlobId>()?.unwrap_string("blobId")?;
|
||||
}
|
||||
_ => {
|
||||
parser.skip_token(parser.depth_array, parser.depth_dict)?;
|
||||
}
|
||||
hashify::fnc_map!(key.as_bytes(),
|
||||
b"accountId" => {
|
||||
self.account_id = map.next_value()?;
|
||||
},
|
||||
b"blobId" => {
|
||||
self.blob_id = map.next_value()?;
|
||||
},
|
||||
_ => {
|
||||
let _ = map.next_value::<serde::de::IgnoredAny>()?;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Ok(request)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ValidateSieveScriptRequest {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_request(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user