jmap: add capabilities module

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
pull/405/head
Manos Pitsidianakis 4 months ago
parent 4722d7ccb8
commit dce3852fe5
No known key found for this signature in database
GPG Key ID: 7729C7707F7E09D0

@ -25,6 +25,7 @@ use isahc::config::Configurable;
use super::*;
use crate::error::NetworkErrorKind;
use crate::jmap::{protocol::JmapMailCapability, rfc8620::capabilities::*};
#[derive(Debug)]
pub struct JmapConnection {
@ -188,47 +189,34 @@ impl JmapConnection {
}
Ok(s) => s,
};
if !session.capabilities.contains_key(JMAP_CORE_CAPABILITY) {
let err = Error::new(format!(
"Server {} did not return JMAP Core capability ({core_capability}). Returned \
capabilities were: {}",
&self.server_conf.server_url,
session
.capabilities
.keys()
.map(String::as_str)
.collect::<Vec<&str>>()
.join(", "),
core_capability = JMAP_CORE_CAPABILITY
));
_ = self
.store
.online_status
.set(Some(req_instant), Err(err.clone()))
.await;
return Err(err);
}
if !session.capabilities.contains_key(JMAP_MAIL_CAPABILITY) {
let err = Error::new(format!(
"Server {} does not support JMAP Mail capability ({mail_capability}). Returned \
capabilities were: {}",
&self.server_conf.server_url,
session
.capabilities
.keys()
.map(String::as_str)
.collect::<Vec<&str>>()
.join(", "),
mail_capability = JMAP_MAIL_CAPABILITY
));
_ = self
.store
.online_status
.set(Some(req_instant), Err(err.clone()))
.await;
return Err(err);
macro_rules! check_for_cap {
($cap:ident) => {{
if !session.capabilities.contains_key($cap::URI) {
let err = Error::new(format!(
"Server {} did not return {name} ({uri}). Returned capabilities were: {}",
&self.server_conf.server_url,
session
.capabilities
.keys()
.map(String::as_str)
.collect::<Vec<&str>>()
.join(", "),
name = $cap::NAME,
uri = $cap::URI
));
_ = self
.store
.online_status
.set(Some(req_instant), Err(err.clone()))
.await;
return Err(err);
}
}};
}
check_for_cap! { JmapCoreCapability };
check_for_cap! { JmapMailCapability };
self.store
.core_capabilities
.lock()

@ -55,30 +55,26 @@ use crate::{
#[macro_export]
macro_rules! _impl {
($(#[$outer:meta])*$field:ident : $t:ty) => {
$(#[$outer])*
($(#[$outer:meta])*$field:ident : $t:ty) => {
$(#[$outer])*
pub fn $field(mut self, new_val: $t) -> Self {
self.$field = new_val;
self
}
};
(get_mut $(#[$outer:meta])*$method:ident, $field:ident : $t:ty) => {
$(#[$outer])*
};
(get_mut $(#[$outer:meta])*$method:ident, $field:ident : $t:ty) => {
$(#[$outer])*
pub fn $method(&mut self) -> &mut $t {
&mut self.$field
}
};
(get $(#[$outer:meta])*$method:ident, $field:ident : $t:ty) => {
$(#[$outer])*
};
(get $(#[$outer:meta])*$method:ident, $field:ident : $t:ty) => {
$(#[$outer])*
pub fn $method(&self) -> &$t {
&self.$field
}
}
}
pub const JMAP_CORE_CAPABILITY: &str = "urn:ietf:params:jmap:core";
pub const JMAP_MAIL_CAPABILITY: &str = "urn:ietf:params:jmap:mail";
pub const JMAP_SUBMISSION_CAPABILITY: &str = "urn:ietf:params:jmap:submission";
}
pub mod operations;
use operations::*;
@ -376,7 +372,7 @@ impl MailBackend for JmapType {
.store
.core_capabilities
.lock()
.map(|c| c.contains_key(JMAP_SUBMISSION_CAPABILITY))
.map(|c| c.contains_key(JmapSubmissionCapability::uri()))
.unwrap_or(false);
MailBackendCapabilities {
supports_submission: CAPABILITIES.supports_submission || supports_submission,
@ -404,7 +400,7 @@ impl MailBackend for JmapType {
Ok(Box::pin(async_stream::try_stream! {
let mut conn = connection.lock().await;
conn.connect().await?;
let batch_size: u64 = conn.store.core_capabilities.lock().unwrap()[JMAP_CORE_CAPABILITY].max_objects_in_get;
let batch_size: u64 = conn.store.core_capabilities.lock().unwrap()[JmapCoreCapability::uri()].max_objects_in_get;
let mut fetch_state = protocol::EmailFetchState::Start { batch_size };
loop {
let res = fetch_state.fetch(

@ -19,16 +19,21 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::jmap::{
mailbox::JmapMailbox,
rfc8620::{capabilities::*, Object, State},
*,
};
use std::convert::{TryFrom, TryInto};
use serde::Serialize;
use serde_json::Value;
use super::{mailbox::JmapMailbox, *};
pub type UtcDate = String;
use super::rfc8620::{Object, State};
crate::_impl_jmap_capability! { JmapMailCapability: "urn:ietf:params:jmap:mail", name: "Mail" }
crate::_impl_jmap_capability! { JmapSubmissionCapability: "urn:ietf:params:jmap:submission", name: "Submission" }
pub trait Response<OBJ: Object>: Send + Sync {
const NAME: &'static str;
@ -38,14 +43,16 @@ pub trait Method<OBJ: Object>: Serialize + Send + Sync {
const NAME: &'static str;
}
static USING: &[&str] = &[JMAP_CORE_CAPABILITY, JMAP_MAIL_CAPABILITY];
static USING: &[&str] = &[JmapCoreCapability::uri(), JmapMailCapability::uri()];
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Request {
using: &'static [&'static str],
/* Why is this Value instead of Box<dyn Method<_>>? The Method trait cannot be made into a
* Trait object because its serialize() will be generic. */
/// This field is `Value` instead of `Box<dyn Method<_>>` because the
/// `Method` trait cannot be made into a trait object; that requires its
/// `serialize()` implementation to be generic.
method_calls: Vec<Value>,
#[serde(skip)]

@ -49,6 +49,8 @@ pub use argument::*;
#[cfg(test)]
mod tests;
pub mod capabilities;
pub type PatchObject = Value;
impl Object for PatchObject {

@ -0,0 +1,93 @@
//
// meli
//
// Copyright 2024 Emmanouil Pitsidianakis <manos@pitsidianak.is>
//
// This file is part of meli.
//
// meli is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// meli is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with meli. If not, see <http://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: EUPL-1.2 OR GPL-3.0-or-later
use serde::{de::DeserializeOwned, ser::Serialize};
pub trait Capability:
Clone
+ Copy
+ std::fmt::Debug
+ PartialEq
+ Eq
+ std::hash::Hash
+ Serialize
+ DeserializeOwned
+ Send
+ Sync
{
const URI: &'static str;
const NAME: &'static str;
}
#[macro_export]
macro_rules! _impl_jmap_capability {
($(#[$outer:meta])*$ident:ident : $key:literal, name: $name:literal) => {
$(#[$outer])*
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct $ident;
impl $crate::jmap::rfc8620::capabilities::Capability for $ident {
const URI: &'static str = $key;
const NAME: &'static str = $name;
}
impl $ident {
pub const fn uri() -> &'static str {
<Self as $crate::jmap::rfc8620::capabilities::Capability>::URI
}
pub const fn name() -> &'static str {
<Self as $crate::jmap::rfc8620::capabilities::Capability>::NAME
}
}
impl ::serde::ser::Serialize for $ident {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: ::serde::ser::Serializer,
{
serializer.serialize_str(<Self as $crate::jmap::rfc8620::capabilities::Capability>::URI)
}
}
impl<'de> ::serde::de::Deserialize<'de> for $ident {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: ::serde::de::Deserializer<'de>,
{
if <&'_ str>::deserialize(deserializer)? == <Self as $crate::jmap::rfc8620::capabilities::Capability>::URI {
return Ok(Self);
}
Err(::serde::de::Error::custom(concat!("Expected string with value \"", $key, '"')))
}
}
impl ::std::fmt::Display for $ident {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(fmt, "JMAP {} Capability", Self::name())
}
}
};
}
_impl_jmap_capability! { JmapCoreCapability: "urn:ietf:params:jmap:core", name: "Core" }

@ -26,8 +26,9 @@ use serde_json::Value;
use url::Url;
use crate::jmap::{
protocol::JmapMailCapability,
rfc8620::{Account, Id, Object, State},
IdentityObject, JMAP_MAIL_CAPABILITY,
IdentityObject,
};
#[derive(Clone, Debug, Deserialize, Serialize)]
@ -59,10 +60,10 @@ impl Session {
self.identities.keys().next().cloned()
}
/// Return the account ID corresponding to the [`JMAP_MAIL_CAPABILITY`]
/// Return the account ID corresponding to the [`JmapMailCapability`]
/// capability.
pub fn mail_account_id(&self) -> Id<Account> {
self.primary_accounts[JMAP_MAIL_CAPABILITY].clone()
self.primary_accounts[JmapMailCapability::uri()].clone()
}
}

Loading…
Cancel
Save