use getset::Getters;
use reqwest::header::InvalidHeaderValue;
use serde::{ser::SerializeStruct, Serialize, Serializer};
use std::fmt;
pub(crate) type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Getters, thiserror::Error)]
#[getset(get = "pub")]
pub struct Error {
uri: String,
title: String,
status: usize,
detail: String,
source: Option<anyhow::Error>,
}
impl Error {
fn new<T, U, V>(
uri: T,
title: U,
status: usize,
detail: V,
source: Option<anyhow::Error>,
) -> Self
where
T: Into<String>,
U: Into<String>,
V: Into<String>,
{
Self {
uri: uri.into(),
title: title.into(),
status,
detail: detail.into(),
source,
}
}
pub(crate) fn invalid_body(detail: String) -> Self {
Self::new("/v1/s/error/invalidbody", "Invalid Body", 400, detail, None)
}
#[cfg(feature = "wrapped")]
pub(crate) fn wrapped_response() -> Self {
Self::new(
"/v1/s/error/wrappedresponse",
"Invalid Wrapped Response",
500,
"Invalid wrapped response received from Vault",
None,
)
}
#[cfg(feature = "wrapped")]
pub(crate) fn token_wrap_failed(error: anyhow::Error) -> Self {
Self::new(
"/v1/s/error/tokenwrapfailed",
"Token Wrap Failed",
500,
"Vault was unable to generate a wrapping token",
Some(error),
)
}
#[cfg(feature = "check_approle")]
pub(crate) fn approles_check_failed(error: anyhow::Error) -> Self {
Self::new(
"/v1/s/error/approlescheckfailed",
"Approle Check Failed",
500,
"Unable to verify the given approle against Vault",
Some(error),
)
}
#[cfg(feature = "check_approle")]
pub(crate) fn approles_response() -> Self {
Self::new(
"/v1/s/error/approlecheckfailed",
"Invalid Approles Response",
500,
"Invalid approles response received from toad",
None,
)
}
pub(crate) fn token_unwrap_failed(error: anyhow::Error) -> Self {
Self::new(
"/v1/s/error/tokenunwrapfailed",
"Token Unwrap Failed",
500,
"Vault was unable to unwrap the given wrapped token",
Some(error),
)
}
pub(crate) fn auth_failed(error: anyhow::Error) -> Self {
Self::new(
"/v1/s/error/authfailed",
"Authentication Failed",
500,
"Vault was unable to authenticate the client",
Some(error),
)
}
pub(crate) fn vault_auth() -> Self {
Self::new(
"/v1/s/error/vaultauth",
"Invalid Vault Auth Response",
500,
"Invalid auth response received from Vault",
None,
)
}
pub(crate) fn data_retrieval_failed(error: anyhow::Error) -> Self {
Self::new(
"/v1/s/error/dataretrievefailed",
"Secret Data Retrieval Failed",
500,
"Vault was unable to retrieve and parse the secret data",
Some(error),
)
}
pub(crate) fn vault_data() -> Self {
Self::new(
"/v1/s/error/vaultdata",
"Invalid Vault Data Response",
500,
"Invalid data response received from Vault",
None,
)
}
pub(crate) fn token_revoke_failed(error: anyhow::Error) -> Self {
Self::new(
"/v1/s/error/tokenrevokefailed",
"Token Revoke Failed",
500,
"Vault was unable to revoke the given token",
Some(error),
)
}
pub(crate) fn reqwest_error(error: reqwest::Error) -> Self {
Self::new(
"/v1/s/error/reqwest",
"reqwest",
500,
"An error from the reqwest library",
Some(error.into()),
)
}
pub(crate) fn invalid_header_error(error: InvalidHeaderValue) -> Self {
Self::new(
"/v1/s/error/invalidheadervalue",
"invalidheadervalue",
500,
"An error converting to a 'HeaderValue'",
Some(error.into()),
)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "uri: {}, ", self.uri)?;
write!(f, "title: {}, ", self.title)?;
write!(f, "status: {}, ", self.status)?;
write!(f, "detail: {}", self.detail)?;
if let Some(ref source) = self.source {
writeln!(f)?;
write!(f, "source: {source:?}")?;
}
Ok(())
}
}
impl Serialize for Error {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let len = if self.source.is_some() { 6 } else { 5 };
let mut state = serializer.serialize_struct("Error", len)?;
state.serialize_field("type", &self.uri)?;
state.serialize_field("title", &self.title)?;
state.serialize_field("status", &self.status)?;
state.serialize_field("detail", &self.detail)?;
if let Some(source) = self.source() {
state.serialize_field("source", &format!("{source}"))?;
}
state.end()
}
}