mirror of https://git.meli.delivery/meli/meli
parent
7eed944abc
commit
21c622a9a4
@ -0,0 +1,224 @@
|
||||
//
|
||||
// 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 indexmap::IndexMap;
|
||||
|
||||
pub type Result<T> = Result<T, JmapError>;
|
||||
|
||||
//{
|
||||
// "type": "urn:ietf:params:jmap:error:unknownCapability",
|
||||
// "status": 400,
|
||||
// "detail": "The Request object used capability
|
||||
// 'https://example.com/apis/foobar', which is not supported
|
||||
// by this server."
|
||||
// }
|
||||
//}
|
||||
|
||||
//A problem details object can have the following members:
|
||||
//
|
||||
//
|
||||
///
|
||||
//
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ProblemDetails {
|
||||
/// ```text
|
||||
/// o "type" (string) - A URI reference [RFC3986] that identifies the
|
||||
/// problem type. This specification encourages that, when
|
||||
/// dereferenced, it provide human-readable documentation for the
|
||||
/// problem type (e.g., using HTML [W3C.REC-html5-20141028]). When
|
||||
/// this member is not present, its value is assumed to be
|
||||
/// "about:blank".
|
||||
/// ```
|
||||
#[serde(default, skip_serializing_if = "ProblemDetailsType::is_blank")]
|
||||
pub r#type: ProblemDetailsType,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
/// ```text
|
||||
/// o "status" (number) - The HTTP status code ([RFC7231], Section 6)
|
||||
/// generated by the origin server for this occurrence of the problem.
|
||||
/// ```
|
||||
pub status: Option<u16>,
|
||||
/// ```text
|
||||
/// o "title" (string) - A short, human-readable summary of the problem
|
||||
/// type. It SHOULD NOT change from occurrence to occurrence of the
|
||||
/// problem, except for purposes of localization (e.g., using
|
||||
/// proactive content negotiation; see [RFC7231], Section 3.4).
|
||||
/// ```
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub title: Option<String>,
|
||||
/// ```text
|
||||
/// o "detail" (string) - A human-readable explanation specific to this
|
||||
/// occurrence of the problem.
|
||||
/// ```
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub detail: Option<String>,
|
||||
/// ```text
|
||||
/// o "instance" (string) - A URI reference that identifies the specific
|
||||
/// occurrence of the problem. It may or may not yield further
|
||||
/// information if dereferenced.
|
||||
/// ```
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub instance: Option<String>,
|
||||
/// Specific errors may have more fields and hence metadata.
|
||||
#[serde(flatten, skip_serializing_if = "IndexMap::is_empty")]
|
||||
pub extra_properties: IndexMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
impl ProblemDetails {
|
||||
pub const CONTENT_TYPE: &'static str = "application/problem+json";
|
||||
}
|
||||
|
||||
//3.6.1. Request-Level Errors
|
||||
//
|
||||
// When an HTTP error response is returned to the client, the server
|
||||
// SHOULD return a JSON "problem details" object as the response body,
|
||||
// as per [RFC7807].
|
||||
//
|
||||
// The following problem types are defined:
|
||||
//
|
||||
// o "urn:ietf:params:jmap:error:unknownCapability"
|
||||
// The client included a capability in the "using" property of the
|
||||
// request that the server does not support.
|
||||
//
|
||||
// o "urn:ietf:params:jmap:error:notJSON"
|
||||
// The content type of the request was not "application/json" or the
|
||||
// request did not parse as I-JSON.
|
||||
//
|
||||
// o "urn:ietf:params:jmap:error:notRequest"
|
||||
// The request parsed as JSON but did not match the type signature of
|
||||
// the Request object.
|
||||
//
|
||||
// o "urn:ietf:params:jmap:error:limit"
|
||||
// The request was not processed as it would have exceeded one of the
|
||||
// request limits defined on the capability object, such as
|
||||
// maxSizeRequest, maxCallsInRequest, or maxConcurrentRequests. A
|
||||
// "limit" property MUST also be present on the "problem details"
|
||||
// object, containing the name of the limit being applied.
|
||||
//
|
||||
//3.6.1.1. Example
|
||||
//
|
||||
// {
|
||||
// "type": "urn:ietf:params:jmap:error:unknownCapability",
|
||||
// "status": 400,
|
||||
// "detail": "The Request object used capability
|
||||
// 'https://example.com/apis/foobar', which is not supported
|
||||
// by this server."
|
||||
// }
|
||||
//
|
||||
//
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
|
||||
#[repr(u8)]
|
||||
pub enum ProblemDetailsType {
|
||||
#[default]
|
||||
Blank = 0,
|
||||
UnknownCapability = 1,
|
||||
NotJson = 2,
|
||||
NotRequest = 3,
|
||||
Limit = 4,
|
||||
}
|
||||
|
||||
impl ProblemDetailsType {
|
||||
pub const URIS: [&'static str; 5] = [
|
||||
"about:blank",
|
||||
"urn:ietf:params:jmap:error:unknownCapability",
|
||||
"urn:ietf:params:jmap:error:notJSON",
|
||||
"urn:ietf:params:jmap:error:notRequest",
|
||||
"urn:ietf:params:jmap:error:limit",
|
||||
];
|
||||
|
||||
pub const fn is_blank(&self) -> bool {
|
||||
matches!(self, Self::Blank)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::serde::ser::Serialize for ProblemDetailsType {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ::serde::ser::Serializer,
|
||||
{
|
||||
serializer.serialize_str(Self::URIS[*self as u8 as usize])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> ::serde::de::Deserialize<'de> for ProblemDetailsType {
|
||||
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||
where
|
||||
D: ::serde::de::Deserializer<'de>,
|
||||
{
|
||||
use serde::de::{Error, Unexpected, Visitor};
|
||||
use ProblemDetailsType as S;
|
||||
|
||||
struct _Visitor;
|
||||
|
||||
impl<'de> Visitor<'de> for _Visitor {
|
||||
type Value = S;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str(
|
||||
"a string representing a valid Problem Type URI as per RFC7807 - Problem \
|
||||
Details for HTTP APIs",
|
||||
)
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, s: &str) -> std::result::Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
Ok(match s {
|
||||
_ if s == S::URIS[S::Blank as u8 as usize] => S::Blank,
|
||||
_ if s == S::URIS[S::UnknownCapability as u8 as usize] => S::UnknownCapability,
|
||||
_ if s == S::URIS[S::NotJson as u8 as usize] => S::NotJson,
|
||||
_ if s == S::URIS[S::NotRequest as u8 as usize] => S::NotRequest,
|
||||
_ if s == S::URIS[S::Limit as u8 as usize] => S::Limit,
|
||||
_ => {
|
||||
return Err(Error::invalid_value(
|
||||
Unexpected::Str(s),
|
||||
&format!("Expected one of: {}", S::URIS.as_slice().join(",")).as_str(),
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(_Visitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for ProblemDetailsType {
|
||||
fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
write!(fmt, "{}", Self::URIS[*self as u8 as usize])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum MethodError {
|
||||
RequestTooLarge,
|
||||
InvalidArguments,
|
||||
InvalidResultReference,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum JmapError {
|
||||
Method { inner: MethodError },
|
||||
Request { inner: ProblemDetails },
|
||||
Other,
|
||||
}
|
Loading…
Reference in New Issue