graph_error/
error.rs

1use serde::Serialize;
2use std::fmt::{Display, Formatter};
3
4#[derive(Default, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
5pub struct InnerError {
6    #[serde(skip_serializing_if = "Option::is_none")]
7    pub code: Option<String>,
8    #[serde(rename = "request-id")]
9    #[serde(skip_serializing_if = "Option::is_none")]
10    pub request_id: Option<String>,
11    #[serde(skip_serializing_if = "Option::is_none")]
12    pub date: Option<String>,
13}
14
15/// An error resource included in the error response returned from
16/// Microsoft Graph.
17///
18/// [odata.error resource type](https://docs.microsoft.com/en-us/graph/errors#odataerror-resource-type)
19#[derive(Default, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
20pub struct ErrorStatus {
21    /// An error code string for the error that occurred
22    /// [Code Property](https://docs.microsoft.com/en-us/graph/errors#code-property)
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub code: Option<String>,
25
26    /// A developer ready message about the error that occurred. This should not be displayed to the user directly.
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub message: Option<String>,
29
30    /// Optional. Additional error objects that may be more specific than the top level error.
31    #[serde(rename = "innerError")]
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub inner_error: Option<InnerError>,
34}
35
36#[derive(thiserror::Error, Debug)]
37pub enum HttpResponseErrorMessage {
38    #[error("{0:#?}")]
39    GraphErrorMessage(#[from] ErrorMessage),
40    #[error("{0:#?}")]
41    SerdeJsonError(#[from] serde_json::error::Error),
42    #[error("{0:#?}")]
43    ReqwestError(#[from] reqwest::Error),
44}
45
46#[derive(thiserror::Error, Default, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
47pub struct ErrorMessage {
48    pub error: ErrorStatus,
49}
50
51impl ErrorMessage {
52    pub fn message(&self) -> Option<String> {
53        self.error.message.clone()
54    }
55
56    pub fn code_property(&self) -> Option<String> {
57        self.error.code.clone()
58    }
59
60    pub fn detailed_error_code(&self) -> Option<String> {
61        self.error.inner_error.as_ref()?.code.clone()
62    }
63
64    pub fn inner_error(&self) -> Option<&InnerError> {
65        self.error.inner_error.as_ref()
66    }
67
68    pub fn request_id(&self) -> Option<String> {
69        self.error.inner_error.as_ref()?.request_id.clone()
70    }
71
72    pub fn date(&self) -> Option<String> {
73        self.error.inner_error.as_ref()?.date.clone()
74    }
75}
76
77impl Display for ErrorMessage {
78    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
79        write!(f, "({:#?})", self.error)
80    }
81}
82
83/// [Microsoft Graph API specific errors and HTTP status codes](https://docs.microsoft.com/en-us/graph/errors)
84#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
85pub enum ErrorType {
86    BadRequest,
87    Unauthorized,
88    Forbidden,
89    NotFound,
90    MethodNotAllowed,
91    NotAcceptable,
92    Conflict,
93    Gone,
94    LengthRequired,
95    PreconditionFailed,
96    RequestEntityTooLarge,
97    UnsupportedMediaType,
98    RequestRangeNotSatisfiable,
99    UnprocessableEntity,
100    Locked,
101    TooManyRequests,
102    InternalServerError,
103    NotImplemented,
104    ServiceUnavailable,
105    GatewayTimeout,
106    InsufficientStorage,
107    BandwidthLimitExceeded,
108    UnknownError,
109}
110
111impl ErrorType {}
112
113impl ErrorType {
114    pub fn as_str(&self) -> &str {
115        match *self {
116			ErrorType::BadRequest => "Cannot process the request because it is malformed or incorrect.",
117			ErrorType::Unauthorized => "Required authentication information is either missing or not valid for the resource.",
118			ErrorType::Forbidden => "Access is denied to the requested resource. The user might not have enough permission.",
119			ErrorType::NotFound => "The requested resource doesnt exist.",
120			ErrorType::MethodNotAllowed => "The HTTP method in the request is not allowed on the resource.",
121			ErrorType::NotAcceptable => "This service doesnt support the format requested in the Accept header.",
122			ErrorType::Conflict =>
123				"The current state conflicts with what the request expects. For example, the specified parent folder might not exist",
124			ErrorType::Gone => "The requested resource is no longer available at the server.",
125			ErrorType::LengthRequired => "A Content-Length header is required on the request.",
126			ErrorType::PreconditionFailed =>
127				"A precondition provided in the request (such as an if-match header) does not match the resource's current state.",
128			ErrorType::RequestEntityTooLarge => "The request size exceeds the maximum limit.",
129			ErrorType::UnsupportedMediaType => "The content type of the request is a format that is not supported by the service.",
130			ErrorType::RequestRangeNotSatisfiable => "The specified byte range is invalid or unavailable.",
131			ErrorType::UnprocessableEntity => "Cannot process the request because it is semantically incorrect.",
132			ErrorType::Locked => "The resource that is being accessed is locked.",
133			ErrorType::TooManyRequests =>
134				"Client application has been throttled and should not attempt to repeat the request until an amount of time has elapsed.",
135			ErrorType::InternalServerError => "There was an internal server error while processing the request.",
136			ErrorType::NotImplemented => "The requested feature isn’t implemented.",
137			ErrorType::ServiceUnavailable =>
138				"The service is temporarily unavailable. You may repeat the request after a delay. There may be a Retry-After header.",
139			ErrorType::GatewayTimeout =>
140				"The server, while acting as a proxy, did not receive a timely response from the upstream server it needed to access in attempting to complete the request. May occur together with 503.",
141			ErrorType::InsufficientStorage => "The maximum storage quota has been reached.",
142			ErrorType::BandwidthLimitExceeded =>
143				"Your app has been throttled for exceeding the maximum bandwidth cap. Your app can retry the request again after more time has elapsed.",
144			ErrorType::UnknownError => "Unknown error or failure",
145		}
146    }
147
148    pub fn from_u16(num: u16) -> Option<ErrorType> {
149        match num {
150            400 => Some(ErrorType::BadRequest),
151            401 => Some(ErrorType::Unauthorized),
152            403 => Some(ErrorType::Forbidden),
153            404 => Some(ErrorType::NotFound),
154            405 => Some(ErrorType::MethodNotAllowed),
155            406 => Some(ErrorType::NotAcceptable),
156            409 => Some(ErrorType::Conflict),
157            410 => Some(ErrorType::Gone),
158            411 => Some(ErrorType::LengthRequired),
159            412 => Some(ErrorType::PreconditionFailed),
160            413 => Some(ErrorType::RequestEntityTooLarge),
161            415 => Some(ErrorType::UnsupportedMediaType),
162            416 => Some(ErrorType::RequestRangeNotSatisfiable),
163            422 => Some(ErrorType::UnprocessableEntity),
164            423 => Some(ErrorType::Locked),
165            429 => Some(ErrorType::TooManyRequests),
166            500 => Some(ErrorType::InternalServerError),
167            501 => Some(ErrorType::NotImplemented),
168            503 => Some(ErrorType::ServiceUnavailable),
169            504 => Some(ErrorType::GatewayTimeout),
170            507 => Some(ErrorType::InsufficientStorage),
171            509 => Some(ErrorType::BandwidthLimitExceeded),
172            _ => None,
173        }
174    }
175
176    pub fn is_error(status: u16) -> bool {
177        ErrorType::from_u16(status).is_some()
178    }
179}
180
181impl Display for ErrorType {
182    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183        write!(f, "{}", self.as_str())
184    }
185}