wolfcose 0.1.0

Safe Rust API for wolfSSL wolfCOSE.
use crate::{CborDeserialize, CborDeserializer, CborMajorType, Error};
use alloc::string::String;
use core::fmt;

/// Detailed result type.
pub type DetailedResult<T> = core::result::Result<T, DetailedError>;

/// Additional context for errors produced by opt-in detailed APIs.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DetailedError {
    /// Underlying stable error.
    pub error: Error,
    /// Optional error context.
    pub context: ErrorContext,
}

impl DetailedError {
    /// Create a detailed error.
    pub fn new(error: Error, context: ErrorContext) -> Self {
        Self { error, context }
    }

    /// Return the underlying stable error.
    pub fn into_error(self) -> Error {
        self.error
    }
}

impl From<Error> for DetailedError {
    fn from(error: Error) -> Self {
        Self {
            error,
            context: ErrorContext::None,
        }
    }
}

impl fmt::Display for DetailedError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.context {
            ErrorContext::None => write!(f, "{}", self.error),
            context => write!(f, "{} ({context:?})", self.error),
        }
    }
}

#[cfg(feature = "std")]
impl std::error::Error for DetailedError {}

/// High-level error context.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ErrorContext {
    /// No extra context.
    None,
    /// CBOR context.
    Cbor(CborErrorKind),
    /// COSE context.
    Cose(CoseErrorKind),
    /// Missing named field.
    MissingField(String),
    /// Named field failed.
    Field(String),
}

/// Detailed CBOR error classification.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CborErrorKind {
    /// Unexpected trailing data.
    TrailingData {
        /// Decoder position where the first trailing byte starts.
        position: usize,
        /// Total input length.
        len: usize,
    },
    /// Unexpected major type.
    UnexpectedType {
        /// Expected CBOR major type, if known.
        expected: Option<CborMajorType>,
        /// Actual CBOR major type at the decoder cursor, if available.
        actual: Option<CborMajorType>,
    },
}

/// Detailed COSE error classification.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CoseErrorKind {
    /// Malformed envelope.
    MalformedEnvelope,
    /// Missing detached payload or ciphertext.
    MissingDetachedPayload,
    /// Key does not match requested operation.
    KeyMismatch,
}

/// Deserialize a complete CBOR item with opt-in detailed error context.
pub fn from_slice_detailed<'de, T: CborDeserialize<'de>>(input: &'de [u8]) -> DetailedResult<T> {
    let mut deserializer = CborDeserializer::new(input);
    let value = T::deserialize(&mut deserializer).map_err(|error| {
        let actual = deserializer.decoder_mut().peek_type();
        DetailedError::new(
            error,
            ErrorContext::Cbor(CborErrorKind::UnexpectedType {
                expected: None,
                actual,
            }),
        )
    })?;
    if deserializer.is_finished() {
        Ok(value)
    } else {
        Err(DetailedError::new(
            Error::CborMalformed,
            ErrorContext::Cbor(CborErrorKind::TrailingData {
                position: deserializer.position(),
                len: input.len(),
            }),
        ))
    }
}