use std::str::Utf8Error;
use crate::response::Response;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ExtractPathParamsError {
#[error(transparent)]
InvalidUtf8InPathParameter(InvalidUtf8InPathParam),
#[error(transparent)]
PathDeserializationError(PathDeserializationError),
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
#[error(
"`{invalid_raw_segment}` cannot be used as `{invalid_key}` \
since it is not a well-formed UTF8 string when percent-decoded"
)]
pub struct InvalidUtf8InPathParam {
pub(super) invalid_key: String,
pub(super) invalid_raw_segment: String,
#[source]
pub(super) source: Utf8Error,
}
#[derive(Debug, thiserror::Error)]
#[error("`{invalid_raw_segment}` is not a well-formed UTF8 string when percent-decoded")]
pub struct DecodeError {
pub(super) invalid_raw_segment: String,
#[source]
pub(super) source: Utf8Error,
}
impl ExtractPathParamsError {
pub fn into_response(&self) -> Response {
match self {
ExtractPathParamsError::InvalidUtf8InPathParameter(e) => {
Response::bad_request().set_typed_body(format!("Invalid URL.\n{}", e))
}
ExtractPathParamsError::PathDeserializationError(e) => match e.kind {
ErrorKind::ParseErrorAtKey { .. } | ErrorKind::ParseError { .. } => {
Response::bad_request().set_typed_body(format!("Invalid URL.\n{}", e.kind))
}
ErrorKind::Message(_) | ErrorKind::UnsupportedType { .. } => {
Response::internal_server_error()
.set_typed_body("Something went wrong when trying to process the request")
}
},
}
}
}
#[derive(Debug)]
pub struct PathDeserializationError {
pub(super) kind: ErrorKind,
}
impl PathDeserializationError {
pub(super) fn new(kind: ErrorKind) -> Self {
Self { kind }
}
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
#[track_caller]
pub(super) fn unsupported_type(name: &'static str) -> Self {
Self::new(ErrorKind::UnsupportedType { name })
}
}
impl serde::de::Error for PathDeserializationError {
#[inline]
fn custom<T>(msg: T) -> Self
where
T: std::fmt::Display,
{
Self {
kind: ErrorKind::Message(msg.to_string()),
}
}
}
impl std::fmt::Display for PathDeserializationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.kind, f)
}
}
impl std::error::Error for PathDeserializationError {}
#[derive(Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ErrorKind {
ParseErrorAtKey {
key: String,
value: String,
expected_type: &'static str,
},
ParseError {
value: String,
expected_type: &'static str,
},
UnsupportedType {
name: &'static str,
},
Message(String),
}
impl std::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ErrorKind::Message(error) => std::fmt::Display::fmt(error, f),
ErrorKind::UnsupportedType { name } => {
write!(
f,
"`{name}` is not a supported type for the `PathParams` extractor. \
The type `T` in `Path<T>` must be a struct (with one public field for each \
templated path segment) or a map (e.g. `HashMap<&'a str, Cow<'a, str>>`)."
)
}
ErrorKind::ParseErrorAtKey {
key,
value,
expected_type,
} => write!(
f,
"`{key}` is set to `{value}`, which we can't parse as a `{expected_type}`"
),
ErrorKind::ParseError {
value,
expected_type,
} => write!(f, "We can't parse `{value}` as a `{expected_type}`"),
}
}
}