use core::any::type_name;
use core::fmt::{Display, Formatter};
use ciborium::value::Value;
use coset::{CoseError, Label};
use strum_macros::IntoStaticStr;
#[cfg(not(feature = "std"))]
use {
alloc::format, alloc::string::String, alloc::string::ToString, core::num::TryFromIntError,
derive_builder::export::core::marker::PhantomData,
};
#[cfg(feature = "std")]
use {std::marker::PhantomData, std::num::TryFromIntError};
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct WrongSourceTypeError<T> {
pub expected_type: &'static str,
pub actual_type: &'static str,
pub general_type: PhantomData<T>,
}
impl<T> Display for WrongSourceTypeError<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(
f,
"the given {} is a {} variant (expected {} variant)",
type_name::<T>(),
self.actual_type,
self.expected_type
)
}
}
impl<T> WrongSourceTypeError<T> {
#[must_use]
pub fn new(expected_type: &'static str, actual_type: &'static str) -> WrongSourceTypeError<T> {
WrongSourceTypeError {
expected_type,
actual_type,
general_type: PhantomData,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct TryFromCborMapError {
message: String,
}
impl Display for TryFromCborMapError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.message)
}
}
impl TryFromCborMapError {
#[must_use]
pub(crate) fn from_message<T>(message: T) -> TryFromCborMapError
where
T: Into<String>,
{
TryFromCborMapError {
message: message.into(),
}
}
#[must_use]
pub(crate) fn unknown_field(key: u8) -> TryFromCborMapError {
TryFromCborMapError {
message: format!("unknown field with key {key} encountered"),
}
}
#[must_use]
pub(crate) fn build_failed<T>(name: &'static str, builder_error: T) -> TryFromCborMapError
where
T: Display,
{
TryFromCborMapError {
message: format!("couldn't build {name}: {builder_error}"),
}
}
}
impl From<TryFromIntError> for TryFromCborMapError {
fn from(e: TryFromIntError) -> Self {
TryFromCborMapError::from_message(e.to_string())
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct ValueIsNotIntegerError;
impl Display for ValueIsNotIntegerError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "CBOR map key must be an integer")
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash, IntoStaticStr)]
pub enum InvalidTextEncodedScopeError {
StartsWithSeparator,
EndsWithSeparator,
ConsecutiveSeparators,
EmptyElement,
EmptyScope,
IllegalCharacters,
Other(&'static str),
}
impl Display for InvalidTextEncodedScopeError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let message = match self {
InvalidTextEncodedScopeError::StartsWithSeparator => "must not start with space",
InvalidTextEncodedScopeError::EndsWithSeparator => "must not end with space",
InvalidTextEncodedScopeError::ConsecutiveSeparators => {
"must not contain consecutive spaces"
}
InvalidTextEncodedScopeError::EmptyElement => "must not contain empty elements",
InvalidTextEncodedScopeError::EmptyScope => "must not be empty",
InvalidTextEncodedScopeError::IllegalCharacters => {
"must not contain illegal character '\\' or '\"'"
}
InvalidTextEncodedScopeError::Other(s) => s,
};
write!(
f,
"text-encoded scope must follow format specified in RFC 6749 ({message})"
)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum InvalidBinaryEncodedScopeError {
StartsWithSeparator(u8),
EndsWithSeparator(u8),
ConsecutiveSeparators(u8),
EmptyScope,
}
impl Display for InvalidBinaryEncodedScopeError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
InvalidBinaryEncodedScopeError::StartsWithSeparator(s) => {
write!(f, "scope may not start with separator '{s:#x}'")
}
InvalidBinaryEncodedScopeError::EndsWithSeparator(s) => {
write!(f, "scope may not end with separator '{s:#x}'")
}
InvalidBinaryEncodedScopeError::ConsecutiveSeparators(s) => {
write!(f, "scope may not contain separator '{s:#x}' twice in a row")
}
InvalidBinaryEncodedScopeError::EmptyScope => write!(f, "scope may not be empty"),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
#[non_exhaustive]
pub enum InvalidAifEncodedScopeError {
InvalidRestMethodSet,
MalformedArray,
}
impl Display for InvalidAifEncodedScopeError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
InvalidAifEncodedScopeError::InvalidRestMethodSet => {
write!(f, "given REST method bitfield is invalid")
}
InvalidAifEncodedScopeError::MalformedArray => {
write!(f, "given AIF CBOR array is malformed")
}
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
#[non_exhaustive]
pub enum CoseCipherError<T>
where
T: Display,
{
HeaderAlreadySet {
existing_header_name: String,
},
VerificationFailure,
DecryptionFailure,
Other(T),
}
impl<T> CoseCipherError<T>
where
T: Display,
{
#[must_use]
pub fn existing_header_label(label: &Label) -> CoseCipherError<T> {
let existing_header_name = match label {
Label::Int(i) => i.to_string(),
Label::Text(s) => s.to_string(),
};
CoseCipherError::HeaderAlreadySet {
existing_header_name,
}
}
#[must_use]
pub fn existing_header<S>(name: S) -> CoseCipherError<T>
where
S: Into<String>,
{
CoseCipherError::HeaderAlreadySet {
existing_header_name: name.into(),
}
}
#[must_use]
pub fn other_error(other: T) -> CoseCipherError<T> {
CoseCipherError::Other(other)
}
pub(crate) fn from_kek_error<C: Display>(
error: CoseCipherError<T>,
) -> CoseCipherError<MultipleCoseError<T, C>> {
match error {
CoseCipherError::Other(x) => CoseCipherError::Other(MultipleCoseError::KekError(x)),
CoseCipherError::HeaderAlreadySet {
existing_header_name,
} => CoseCipherError::HeaderAlreadySet {
existing_header_name,
},
CoseCipherError::VerificationFailure => CoseCipherError::VerificationFailure,
CoseCipherError::DecryptionFailure => CoseCipherError::DecryptionFailure,
}
}
pub(crate) fn from_cek_error<K: Display>(
error: CoseCipherError<T>,
) -> CoseCipherError<MultipleCoseError<K, T>> {
match error {
CoseCipherError::Other(x) => CoseCipherError::Other(MultipleCoseError::CekError(x)),
CoseCipherError::HeaderAlreadySet {
existing_header_name,
} => CoseCipherError::HeaderAlreadySet {
existing_header_name,
},
CoseCipherError::VerificationFailure => CoseCipherError::VerificationFailure,
CoseCipherError::DecryptionFailure => CoseCipherError::DecryptionFailure,
}
}
}
impl<T> Display for CoseCipherError<T>
where
T: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
CoseCipherError::HeaderAlreadySet {
existing_header_name,
} => write!(
f,
"cipher-defined header '{existing_header_name}' already set"
),
CoseCipherError::VerificationFailure => write!(f, "data verification failed"),
CoseCipherError::DecryptionFailure => write!(f, "decryption failed"),
CoseCipherError::Other(s) => write!(f, "{s}"),
}
}
}
#[derive(Debug)]
pub enum MultipleCoseError<K, C>
where
K: Display,
C: Display,
{
KekError(K),
CekError(C),
}
impl<K, C> Display for MultipleCoseError<K, C>
where
K: Display,
C: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
MultipleCoseError::KekError(k) => k.fmt(f),
MultipleCoseError::CekError(c) => c.fmt(f),
}
}
}
#[derive(Debug, PartialEq, Clone)]
#[non_exhaustive]
pub enum ScopeFromValueError {
InvalidBinaryEncodedScope(InvalidBinaryEncodedScopeError),
InvalidTextEncodedScope(InvalidTextEncodedScopeError),
InvalidAifEncodedScope(InvalidAifEncodedScopeError),
InvalidType(WrongSourceTypeError<Value>),
}
fn to_variant_name(value: &Value) -> &'static str {
match value {
Value::Integer(_) => "Integer",
Value::Bytes(_) => "Bytes",
Value::Float(_) => "Float",
Value::Text(_) => "Text",
Value::Bool(_) => "Bool",
Value::Null => "Null",
Value::Tag(_, _) => "Tag",
Value::Array(_) => "Array",
Value::Map(_) => "Map",
_ => "Unknown",
}
}
impl ScopeFromValueError {
#[must_use]
pub fn invalid_type(actual: &Value) -> ScopeFromValueError {
ScopeFromValueError::from(WrongSourceTypeError::new(
"Text or Bytes",
to_variant_name(actual),
))
}
}
impl From<InvalidTextEncodedScopeError> for ScopeFromValueError {
fn from(err: InvalidTextEncodedScopeError) -> Self {
ScopeFromValueError::InvalidTextEncodedScope(err)
}
}
impl From<InvalidBinaryEncodedScopeError> for ScopeFromValueError {
fn from(err: InvalidBinaryEncodedScopeError) -> Self {
ScopeFromValueError::InvalidBinaryEncodedScope(err)
}
}
impl From<InvalidAifEncodedScopeError> for ScopeFromValueError {
fn from(err: InvalidAifEncodedScopeError) -> Self {
ScopeFromValueError::InvalidAifEncodedScope(err)
}
}
impl From<WrongSourceTypeError<Value>> for ScopeFromValueError {
fn from(err: WrongSourceTypeError<Value>) -> Self {
ScopeFromValueError::InvalidType(err)
}
}
impl Display for ScopeFromValueError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
ScopeFromValueError::InvalidBinaryEncodedScope(s) => {
write!(f, "invalid binary-encoded scope: {s}")
}
ScopeFromValueError::InvalidTextEncodedScope(s) => {
write!(f, "invalid text-encoded scope: {s}")
}
ScopeFromValueError::InvalidType(t) => write!(f, "invalid type: {t}"),
ScopeFromValueError::InvalidAifEncodedScope(a) => {
write!(f, "invalid AIF-encoded scope: {a}")
}
}
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum AccessTokenError<T>
where
T: Display,
{
CoseError(CoseError),
CoseCipherError(CoseCipherError<T>),
UnknownCoseStructure,
NoMatchingRecipient,
MultipleMatchingRecipients,
}
impl<T> Display for AccessTokenError<T>
where
T: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
AccessTokenError::CoseError(e) => write!(f, "{e}"),
AccessTokenError::CoseCipherError(e) => write!(f, "cipher error: {e}"),
AccessTokenError::UnknownCoseStructure => write!(
f,
"input is either invalid or none of CoseEncrypt0, CoseSign1 nor CoseMac0"
),
AccessTokenError::NoMatchingRecipient => {
write!(f, "given KEK doesn't match any recipient")
}
AccessTokenError::MultipleMatchingRecipients => {
write!(f, "given KEK matches multiple recipients")
}
}
}
}
impl<T> From<CoseCipherError<T>> for AccessTokenError<T>
where
T: Display,
{
#[must_use]
fn from(error: CoseCipherError<T>) -> Self {
AccessTokenError::CoseCipherError(error)
}
}
impl<T> From<CoseError> for AccessTokenError<T>
where
T: Display,
{
#[must_use]
fn from(error: CoseError) -> Self {
AccessTokenError::CoseError(error)
}
}
#[allow(dead_code)]
impl<T> AccessTokenError<T>
where
T: Display,
{
pub(crate) fn from_kek_error<C: Display>(
error: AccessTokenError<T>,
) -> AccessTokenError<MultipleCoseError<T, C>> {
match error {
AccessTokenError::CoseCipherError(x) => {
AccessTokenError::CoseCipherError(CoseCipherError::from_kek_error(x))
}
AccessTokenError::CoseError(x) => AccessTokenError::CoseError(x),
AccessTokenError::UnknownCoseStructure => AccessTokenError::UnknownCoseStructure,
AccessTokenError::NoMatchingRecipient => AccessTokenError::NoMatchingRecipient,
AccessTokenError::MultipleMatchingRecipients => {
AccessTokenError::MultipleMatchingRecipients
}
}
}
pub(crate) fn from_cek_error<K: Display>(
error: AccessTokenError<T>,
) -> AccessTokenError<MultipleCoseError<K, T>> {
match error {
AccessTokenError::CoseCipherError(x) => {
AccessTokenError::CoseCipherError(CoseCipherError::from_cek_error(x))
}
AccessTokenError::CoseError(x) => AccessTokenError::CoseError(x),
AccessTokenError::UnknownCoseStructure => AccessTokenError::UnknownCoseStructure,
AccessTokenError::NoMatchingRecipient => AccessTokenError::NoMatchingRecipient,
AccessTokenError::MultipleMatchingRecipients => {
AccessTokenError::MultipleMatchingRecipients
}
}
}
}
#[cfg(feature = "std")]
mod std_error {
use core::fmt::Debug;
use std::error::Error;
use crate::endpoints::creation_hint::AuthServerRequestCreationHintBuilderError;
use crate::endpoints::token_req::AccessTokenRequestBuilderError;
use crate::endpoints::token_req::AccessTokenResponseBuilderError;
use crate::endpoints::token_req::ErrorResponseBuilderError;
use super::*;
impl<T> Error for WrongSourceTypeError<T> where T: Debug {}
impl Error for TryFromCborMapError {}
impl Error for ValueIsNotIntegerError {}
impl Error for InvalidTextEncodedScopeError {}
impl Error for InvalidBinaryEncodedScopeError {}
impl Error for InvalidAifEncodedScopeError {}
impl Error for ScopeFromValueError {}
impl<T> Error for CoseCipherError<T> where T: Debug + Display {}
impl<T> Error for AccessTokenError<T> where T: Debug + Display {}
impl Error for AccessTokenRequestBuilderError {}
impl Error for AccessTokenResponseBuilderError {}
impl Error for ErrorResponseBuilderError {}
impl Error for AuthServerRequestCreationHintBuilderError {}
}