pub use jmap_types::{GetObject, JmapObject, QueryObject, SetObject};
#[non_exhaustive]
#[derive(Debug)]
pub enum BackendChangesError<E> {
TooManyChanges { limit: u64 },
Other(E),
}
impl<E: std::fmt::Display> std::fmt::Display for BackendChangesError<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::TooManyChanges { limit: 0 } => write!(f, "cannot calculate changes"),
Self::TooManyChanges { limit } => write!(f, "too many changes (limit: {limit})"),
Self::Other(e) => write!(f, "{e}"),
}
}
}
impl<E: std::error::Error + 'static> std::error::Error for BackendChangesError<E> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Other(e) => Some(e),
_ => None,
}
}
}
impl<E> From<E> for BackendChangesError<E> {
fn from(e: E) -> Self {
Self::Other(e)
}
}
impl<E: std::error::Error> From<BackendChangesError<E>> for jmap_types::JmapError {
fn from(e: BackendChangesError<E>) -> Self {
match e {
BackendChangesError::TooManyChanges { limit: 0 } => {
jmap_types::JmapError::cannot_calculate_changes()
}
BackendChangesError::TooManyChanges { limit } => {
jmap_types::JmapError::too_many_changes_with_limit(limit)
}
BackendChangesError::Other(inner) => {
jmap_types::JmapError::server_fail(inner.to_string())
}
}
}
}
#[derive(Debug)]
#[non_exhaustive]
pub struct ChangesResult {
pub created: Vec<jmap_types::Id>,
pub updated: Vec<jmap_types::Id>,
pub destroyed: Vec<jmap_types::Id>,
pub has_more_changes: bool,
pub new_state: jmap_types::State,
}
impl ChangesResult {
pub fn new(
created: Vec<jmap_types::Id>,
updated: Vec<jmap_types::Id>,
destroyed: Vec<jmap_types::Id>,
has_more_changes: bool,
new_state: jmap_types::State,
) -> Self {
Self {
created,
updated,
destroyed,
has_more_changes,
new_state,
}
}
}
#[derive(Debug)]
#[non_exhaustive]
pub struct QueryResult {
pub ids: Vec<jmap_types::Id>,
pub position: i64,
pub total: Option<u64>,
pub query_state: jmap_types::State,
pub can_calculate_changes: bool,
}
impl QueryResult {
pub fn new(
ids: Vec<jmap_types::Id>,
position: i64,
total: Option<u64>,
query_state: jmap_types::State,
can_calculate_changes: bool,
) -> Self {
Self {
ids,
position,
total,
query_state,
can_calculate_changes,
}
}
}
#[derive(Debug)]
#[non_exhaustive]
pub struct AddedItem {
pub id: jmap_types::Id,
pub index: u64,
}
impl AddedItem {
pub fn new(id: jmap_types::Id, index: u64) -> Self {
Self { id, index }
}
}
#[derive(Debug)]
#[non_exhaustive]
pub struct QueryChangesResult {
pub old_query_state: jmap_types::State,
pub new_query_state: jmap_types::State,
pub total: Option<u64>,
pub removed: Vec<jmap_types::Id>,
pub added: Vec<AddedItem>,
}
impl QueryChangesResult {
pub fn new(
old_query_state: jmap_types::State,
new_query_state: jmap_types::State,
total: Option<u64>,
removed: Vec<jmap_types::Id>,
added: Vec<AddedItem>,
) -> Self {
Self {
old_query_state,
new_query_state,
total,
removed,
added,
}
}
}
pub trait JmapBackend: Send + Sync + 'static {
type Error: std::error::Error + Send + Sync + 'static;
fn get_objects<O: GetObject + Send + Sync>(
&self,
account_id: &jmap_types::Id,
ids: Option<&[jmap_types::Id]>,
properties: Option<&[<O as JmapObject>::Property]>,
) -> impl std::future::Future<Output = Result<(Vec<O>, Vec<jmap_types::Id>), Self::Error>> + Send;
fn get_state<O: JmapObject + Send + Sync>(
&self,
account_id: &jmap_types::Id,
) -> impl std::future::Future<Output = Result<jmap_types::State, Self::Error>> + Send;
fn get_changes<O: JmapObject + Send + Sync>(
&self,
account_id: &jmap_types::Id,
since_state: &jmap_types::State,
max_changes: Option<u64>,
) -> impl std::future::Future<Output = Result<ChangesResult, BackendChangesError<Self::Error>>> + Send;
fn query_objects<O: QueryObject + Send + Sync>(
&self,
account_id: &jmap_types::Id,
filter: Option<&O::Filter>,
sort: Option<&[O::Comparator]>,
limit: Option<u64>,
position: i64,
) -> impl std::future::Future<Output = Result<QueryResult, Self::Error>> + Send;
#[allow(clippy::too_many_arguments)]
fn query_changes<O: QueryObject + Send + Sync>(
&self,
account_id: &jmap_types::Id,
since_query_state: &jmap_types::State,
filter: Option<&O::Filter>,
sort: Option<&[O::Comparator]>,
max_changes: Option<u64>,
up_to_id: Option<&jmap_types::Id>,
collapse_threads: bool,
) -> impl std::future::Future<
Output = Result<QueryChangesResult, BackendChangesError<Self::Error>>,
> + Send;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn backend_changes_error_limit_zero_maps_to_cannot_calculate() {
let err = jmap_types::JmapError::from(
BackendChangesError::<std::convert::Infallible>::TooManyChanges { limit: 0 },
);
assert_eq!(
err.error_type.as_str(),
"cannotCalculateChanges",
"limit=0 must produce cannotCalculateChanges; got: {:?}",
err.error_type
);
}
#[test]
fn backend_changes_error_nonzero_limit_maps_to_too_many_changes() {
let err = jmap_types::JmapError::from(
BackendChangesError::<std::convert::Infallible>::TooManyChanges { limit: 50 },
);
assert_eq!(
err.error_type.as_str(),
"tooManyChanges",
"limit=50 must produce tooManyChanges; got: {:?}",
err.error_type
);
}
}