/* * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ use serde::Serialize; use serde::ser::SerializeMap; use std::fmt::Display; #[derive(Debug)] pub enum MethodError { InvalidArguments(String), RequestTooLarge, StateMismatch, AnchorNotFound, UnsupportedFilter(String), UnsupportedSort(String), ServerFail(String), UnknownMethod(String), ServerUnavailable, ServerPartialFail, InvalidResultReference(String), Forbidden(String), AccountNotFound, AccountNotSupportedByMethod, AccountReadOnly, NotFound, CannotCalculateChanges, UnknownDataType, } #[derive(Debug)] pub struct MethodErrorWrapper(trc::Error); impl From for MethodErrorWrapper { fn from(value: trc::Error) -> Self { MethodErrorWrapper(value) } } impl Display for MethodError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { MethodError::InvalidArguments(err) => write!(f, "Invalid arguments: {}", err), MethodError::RequestTooLarge => write!(f, "Request too large"), MethodError::StateMismatch => write!(f, "State mismatch"), MethodError::AnchorNotFound => write!(f, "Anchor not found"), MethodError::UnsupportedFilter(err) => write!(f, "Unsupported filter: {}", err), MethodError::UnsupportedSort(err) => write!(f, "Unsupported sort: {}", err), MethodError::ServerFail(err) => write!(f, "Server error: {}", err), MethodError::UnknownMethod(err) => write!(f, "Unknown method: {}", err), MethodError::ServerUnavailable => write!(f, "Server unavailable"), MethodError::ServerPartialFail => write!(f, "Server partial fail"), MethodError::InvalidResultReference(err) => { write!(f, "Invalid result reference: {}", err) } MethodError::Forbidden(err) => write!(f, "Forbidden: {}", err), MethodError::AccountNotFound => write!(f, "Account not found"), MethodError::AccountNotSupportedByMethod => { write!(f, "Account not supported by method") } MethodError::AccountReadOnly => write!(f, "Account read only"), MethodError::NotFound => write!(f, "Not found"), MethodError::UnknownDataType => write!(f, "Unknown data type"), MethodError::CannotCalculateChanges => write!(f, "Cannot calculate changes"), } } } impl Serialize for MethodErrorWrapper { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { let mut map = serializer.serialize_map(2.into())?; let description = self.0.value(trc::Key::Details).and_then(|v| v.as_str()); let (error_type, description) = match self.0.as_ref() { trc::EventType::Jmap(cause) => match cause { trc::JmapEvent::InvalidArguments => { ("invalidArguments", description.unwrap_or_default()) } trc::JmapEvent::RequestTooLarge => ( "requestTooLarge", concat!( "The number of ids requested by the client exceeds the maximum number ", "the server is willing to process in a single method call." ), ), trc::JmapEvent::StateMismatch => ( "stateMismatch", concat!( "An \"ifInState\" argument was supplied, but ", "it does not match the current state." ), ), trc::JmapEvent::AnchorNotFound => ( "anchorNotFound", concat!( "An anchor argument was supplied, but it ", "cannot be found in the results of the query." ), ), trc::JmapEvent::UnsupportedFilter => { ("unsupportedFilter", description.unwrap_or_default()) } trc::JmapEvent::UnsupportedSort => { ("unsupportedSort", description.unwrap_or_default()) } trc::JmapEvent::NotFound => ("serverPartialFail", { concat!( "One or more items are no longer available on the ", "server, please try again." ) }), trc::JmapEvent::UnknownMethod => ("unknownMethod", description.unwrap_or_default()), trc::JmapEvent::InvalidResultReference => { ("invalidResultReference", description.unwrap_or_default()) } trc::JmapEvent::Forbidden => ("forbidden", description.unwrap_or_default()), trc::JmapEvent::AccountNotFound => ( "accountNotFound", "The accountId does not correspond to a valid account", ), trc::JmapEvent::AccountNotSupportedByMethod => ( "accountNotSupportedByMethod", concat!( "The accountId given corresponds to a valid account, ", "but the account does not support this method or data type." ), ), trc::JmapEvent::AccountReadOnly => ( "accountReadOnly", "This method modifies state, but the account is read-only.", ), trc::JmapEvent::UnknownDataType => ( "unknownDataType", concat!( "The server does not recognise this data type, ", "or the capability to enable it is not present ", "in the current Request Object." ), ), trc::JmapEvent::CannotCalculateChanges => ( "cannotCalculateChanges", concat!( "The server cannot calculate the changes ", "between the old and new states." ), ), trc::JmapEvent::UnknownCapability | trc::JmapEvent::NotJson | trc::JmapEvent::NotRequest => ( "serverUnavailable", concat!( "This server is temporarily unavailable. ", "Attempting this same operation later may succeed." ), ), _ => ( "serverUnavailable", "This server is temporarily unavailable.", ), }, _ => ( "serverUnavailable", concat!( "This server is temporarily unavailable. ", "Attempting this same operation later may succeed." ), ), }; map.serialize_entry("type", error_type)?; if !description.is_empty() { map.serialize_entry("description", description)?; } map.end() } }