use std::borrow::Cow;
use thiserror::Error;
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum Error {
#[error("unsupported hash algorithm: {0}")]
UnsupportedAlgorithm(Cow<'static, str>),
#[error("invalid hash string: {0}")]
InvalidHashString(Cow<'static, str>),
#[error("invalid parameter: {0}")]
InvalidParameter(Cow<'static, str>),
#[error("password rejected: {0}")]
InvalidPassword(Cow<'static, str>),
#[error("invalid salt: {0}")]
InvalidSalt(Cow<'static, str>),
#[error("hashing failed: {0}")]
Hashing(HashingError),
#[error("verification failed: {0}")]
Verification(Cow<'static, str>),
#[error("invalid policy: {0}")]
InvalidPolicy(Cow<'static, str>),
#[error(transparent)]
Decode(#[from] DecodeError),
#[cfg(feature = "pepper")]
#[error("pepper provider: {0}")]
Pepper(Cow<'static, str>),
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct HashingError {
pub kind: HashingErrorKind,
pub detail: Cow<'static, str>,
}
impl std::fmt::Display for HashingError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: {}", self.kind, self.detail)
}
}
impl std::error::Error for HashingError {}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum HashingErrorKind {
Argon2,
Bcrypt,
Scrypt,
Pbkdf2,
PhcEncoder,
}
impl std::fmt::Display for HashingErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Argon2 => f.write_str("argon2"),
Self::Bcrypt => f.write_str("bcrypt"),
Self::Scrypt => f.write_str("scrypt"),
Self::Pbkdf2 => f.write_str("pbkdf2"),
Self::PhcEncoder => f.write_str("phc encoder"),
}
}
}
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum DecodeError {
#[error("utf-8 decode: {0}")]
Utf8(Cow<'static, str>),
#[error("base64 decode: {0}")]
Base64(Cow<'static, str>),
#[error("json decode: {0}")]
Json(Cow<'static, str>),
}
pub type Result<T> = std::result::Result<T, Error>;
impl From<std::str::Utf8Error> for Error {
fn from(e: std::str::Utf8Error) -> Self {
Error::Decode(DecodeError::Utf8(e.to_string().into()))
}
}
impl From<base64::DecodeError> for Error {
fn from(e: base64::DecodeError) -> Self {
Error::Decode(DecodeError::Base64(e.to_string().into()))
}
}
impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Self {
Error::Decode(DecodeError::Json(e.to_string().into()))
}
}
#[cfg(feature = "pepper")]
impl From<hsh_kms::PepperError> for Error {
fn from(e: hsh_kms::PepperError) -> Self {
Error::Pepper(e.to_string().into())
}
}
impl Error {
pub fn hashing(
kind: HashingErrorKind,
detail: impl Into<Cow<'static, str>>,
) -> Self {
Self::Hashing(HashingError {
kind,
detail: detail.into(),
})
}
}