use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
pub trait ErrorResponse: Debug + Display + DeserializeOwned + Serialize {}
pub trait ErrorResponseType: Debug + DeserializeOwned + Serialize {}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct StandardErrorResponse<T: ErrorResponseType> {
#[serde(bound = "T: ErrorResponseType")]
pub(crate) error: T,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) error_description: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) error_uri: Option<String>,
}
impl<T: ErrorResponseType> StandardErrorResponse<T> {
pub fn new(error: T, error_description: Option<String>, error_uri: Option<String>) -> Self {
Self {
error,
error_description,
error_uri,
}
}
pub fn error(&self) -> &T {
&self.error
}
pub fn error_description(&self) -> Option<&String> {
self.error_description.as_ref()
}
pub fn error_uri(&self) -> Option<&String> {
self.error_uri.as_ref()
}
}
impl<T> ErrorResponse for StandardErrorResponse<T> where T: ErrorResponseType + Display + 'static {}
impl<TE> Display for StandardErrorResponse<TE>
where
TE: ErrorResponseType + Display,
{
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
let mut formatted = self.error().to_string();
if let Some(error_description) = self.error_description() {
formatted.push_str(": ");
formatted.push_str(error_description);
}
if let Some(error_uri) = self.error_uri() {
formatted.push_str(" (see ");
formatted.push_str(error_uri);
formatted.push(')');
}
write!(f, "{formatted}")
}
}
#[derive(Debug, thiserror::Error)]
pub enum RequestTokenError<RE, T>
where
RE: Error + 'static,
T: ErrorResponse + 'static,
{
#[error("Server returned error response: {0}")]
ServerResponse(T),
#[error("Request failed")]
Request(#[from] RE),
#[error("Failed to parse server response")]
Parse(
#[source] serde_path_to_error::Error<serde_json::error::Error>,
Vec<u8>,
),
#[error("Other error: {}", _0)]
Other(String),
}
#[cfg(test)]
mod tests {
use crate::basic::{BasicErrorResponse, BasicErrorResponseType};
#[test]
fn test_error_response_serializer() {
assert_eq!(
"{\"error\":\"unauthorized_client\"}",
serde_json::to_string(&BasicErrorResponse::new(
BasicErrorResponseType::UnauthorizedClient,
None,
None,
))
.unwrap(),
);
assert_eq!(
"{\
\"error\":\"invalid_client\",\
\"error_description\":\"Invalid client_id\",\
\"error_uri\":\"https://example.com/errors/invalid_client\"\
}",
serde_json::to_string(&BasicErrorResponse::new(
BasicErrorResponseType::InvalidClient,
Some("Invalid client_id".to_string()),
Some("https://example.com/errors/invalid_client".to_string()),
))
.unwrap(),
);
}
}