jmap_client/core/
error.rs

1/*
2 * Copyright Stalwart Labs LLC See the COPYING
3 * file at the top-level directory of this distribution.
4 *
5 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
8 * option. This file may not be copied, modified, or distributed
9 * except according to those terms.
10 */
11
12use std::fmt::Display;
13
14use serde::Deserialize;
15
16#[derive(Debug, Deserialize)]
17pub struct ProblemDetails {
18    #[serde(rename = "type")]
19    p_type: ProblemType,
20    pub status: Option<u32>,
21    title: Option<String>,
22    detail: Option<String>,
23    limit: Option<String>,
24    request_id: Option<String>,
25}
26
27#[derive(Debug, Deserialize)]
28pub enum JMAPError {
29    #[serde(rename = "urn:ietf:params:jmap:error:unknownCapability")]
30    UnknownCapability,
31    #[serde(rename = "urn:ietf:params:jmap:error:notJSON")]
32    NotJSON,
33    #[serde(rename = "urn:ietf:params:jmap:error:notRequest")]
34    NotRequest,
35    #[serde(rename = "urn:ietf:params:jmap:error:limit")]
36    Limit,
37}
38
39#[derive(Debug, Deserialize)]
40#[serde(untagged)]
41pub enum ProblemType {
42    JMAP(JMAPError),
43    Other(String),
44}
45
46#[derive(Debug, Deserialize, PartialEq, Eq)]
47pub struct MethodError {
48    #[serde(rename = "type")]
49    pub p_type: MethodErrorType,
50}
51
52#[derive(Debug, Deserialize, PartialEq, Eq)]
53pub enum MethodErrorType {
54    #[serde(rename = "serverUnavailable")]
55    ServerUnavailable,
56    #[serde(rename = "serverFail")]
57    ServerFail,
58    #[serde(rename = "serverPartialFail")]
59    ServerPartialFail,
60    #[serde(rename = "unknownMethod")]
61    UnknownMethod,
62    #[serde(rename = "invalidArguments")]
63    InvalidArguments,
64    #[serde(rename = "invalidResultReference")]
65    InvalidResultReference,
66    #[serde(rename = "forbidden")]
67    Forbidden,
68    #[serde(rename = "accountNotFound")]
69    AccountNotFound,
70    #[serde(rename = "accountNotSupportedByMethod")]
71    AccountNotSupportedByMethod,
72    #[serde(rename = "accountReadOnly")]
73    AccountReadOnly,
74    #[serde(rename = "requestTooLarge")]
75    RequestTooLarge,
76    #[serde(rename = "cannotCalculateChanges")]
77    CannotCalculateChanges,
78    #[serde(rename = "stateMismatch")]
79    StateMismatch,
80    #[serde(rename = "alreadyExists")]
81    AlreadyExists,
82    #[serde(rename = "fromAccountNotFound")]
83    FromAccountNotFound,
84    #[serde(rename = "fromAccountNotSupportedByMethod")]
85    FromAccountNotSupportedByMethod,
86    #[serde(rename = "anchorNotFound")]
87    AnchorNotFound,
88    #[serde(rename = "unsupportedSort")]
89    UnsupportedSort,
90    #[serde(rename = "unsupportedFilter")]
91    UnsupportedFilter,
92    #[serde(rename = "tooManyChanges")]
93    TooManyChanges,
94}
95
96impl ProblemDetails {
97    pub fn new(
98        p_type: ProblemType,
99        status: Option<u32>,
100        title: Option<String>,
101        detail: Option<String>,
102        limit: Option<String>,
103        request_id: Option<String>,
104    ) -> Self {
105        ProblemDetails {
106            p_type,
107            status,
108            title,
109            detail,
110            limit,
111            request_id,
112        }
113    }
114
115    pub fn error(&self) -> &ProblemType {
116        &self.p_type
117    }
118
119    pub fn status(&self) -> Option<u32> {
120        self.status
121    }
122
123    pub fn title(&self) -> Option<&str> {
124        self.title.as_deref()
125    }
126
127    pub fn detail(&self) -> Option<&str> {
128        self.detail.as_deref()
129    }
130
131    pub fn limit(&self) -> Option<&str> {
132        self.limit.as_deref()
133    }
134
135    pub fn request_id(&self) -> Option<&str> {
136        self.request_id.as_deref()
137    }
138}
139
140impl MethodError {
141    pub fn error(&self) -> &MethodErrorType {
142        &self.p_type
143    }
144}
145
146impl Display for MethodError {
147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148        match self.p_type {
149            MethodErrorType::ServerUnavailable => write!(f, "Server unavailable"),
150            MethodErrorType::ServerFail => write!(f, "Server fail"),
151            MethodErrorType::ServerPartialFail => write!(f, "Server partial fail"),
152            MethodErrorType::UnknownMethod => write!(f, "Unknown method"),
153            MethodErrorType::InvalidArguments => write!(f, "Invalid arguments"),
154            MethodErrorType::InvalidResultReference => write!(f, "Invalid result reference"),
155            MethodErrorType::Forbidden => write!(f, "Forbidden"),
156            MethodErrorType::AccountNotFound => write!(f, "Account not found"),
157            MethodErrorType::AccountNotSupportedByMethod => {
158                write!(f, "Account not supported by method")
159            }
160            MethodErrorType::AccountReadOnly => write!(f, "Account read only"),
161            MethodErrorType::RequestTooLarge => write!(f, "Request too large"),
162            MethodErrorType::CannotCalculateChanges => write!(f, "Cannot calculate changes"),
163            MethodErrorType::StateMismatch => write!(f, "State mismatch"),
164            MethodErrorType::AlreadyExists => write!(f, "Already exists"),
165            MethodErrorType::FromAccountNotFound => write!(f, "From account not found"),
166            MethodErrorType::FromAccountNotSupportedByMethod => {
167                write!(f, "From account not supported by method")
168            }
169            MethodErrorType::AnchorNotFound => write!(f, "Anchor not found"),
170            MethodErrorType::UnsupportedSort => write!(f, "Unsupported sort"),
171            MethodErrorType::UnsupportedFilter => write!(f, "Unsupported filter"),
172            MethodErrorType::TooManyChanges => write!(f, "Too many changes"),
173        }
174    }
175}
176
177impl Display for ProblemDetails {
178    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
179        match &self.p_type {
180            ProblemType::JMAP(err) => match err {
181                JMAPError::UnknownCapability => write!(f, "Unknown capability")?,
182                JMAPError::NotJSON => write!(f, "Not JSON")?,
183                JMAPError::NotRequest => write!(f, "Not request")?,
184                JMAPError::Limit => write!(f, "Limit")?,
185            },
186            ProblemType::Other(err) => f.write_str(err.as_str())?,
187        }
188
189        if let Some(status) = self.status {
190            write!(f, " (status {})", status)?
191        }
192
193        if let Some(title) = &self.title {
194            write!(f, ": {}", title)?
195        }
196
197        if let Some(detail) = &self.detail {
198            write!(f, ". Details: {}", detail)?
199        }
200
201        Ok(())
202    }
203}