evidence 0.1.0

Type-level tags for cryptographic primitives
Documentation
//! Codec traits for encoding and decoding values.
//!
//! Codecs are marker types (e.g., [`Cbor`](cbor::Cbor), [`Json`](json::Json))
//! that implement [`Encode`] and [`Decode`] for the value types they support.
//!
//! The marker types are always available regardless of feature flags.
//! Enable the corresponding feature (`minicbor`, `serde_json`) to get
//! [`Encode`]/[`Decode`] implementations backed by a concrete serialization
//! library.

use alloc::vec::Vec;

pub mod cbor;
pub mod json;

/// A codec that can encode values of type `T` to bytes.
pub trait Encode<T: ?Sized> {
    /// The error type returned when encoding fails.
    type Error: core::fmt::Debug;

    /// Encode `value` into a byte vector.
    ///
    /// # Errors
    ///
    /// Returns an error if the value cannot be serialized to bytes.
    fn encode(value: &T) -> Result<Vec<u8>, Self::Error>;
}

/// A codec that can decode bytes into values of type `T`.
pub trait Decode<T> {
    /// The error type returned when decoding fails.
    type Error;

    /// Decode a value of type `T` from `bytes`.
    ///
    /// # Errors
    ///
    /// Returns an error if the bytes cannot be deserialized into `T`.
    fn decode(bytes: &[u8]) -> Result<T, Self::Error>;
}

/// Marker trait for codecs that produce deterministic (canonical) encodings.
///
/// A canonical codec guarantees that the same value always encodes to
/// identical bytes, which is essential for content-addressed hashing.
pub trait Canonical {}

/// Identity codec that passes bytes through unchanged.
///
/// Useful for testing or when values are already serialized.
///
/// # Example
///
/// ```
/// # #[cfg(feature = "sha2")]
/// # {
/// use evidence::digest::{Digest, sha2::Sha256};
/// use evidence::codec::Identity;
///
/// let data = b"hello world";
/// let digest: Digest<[u8; 11], Sha256, Identity> = Digest::hash(data);
/// assert_eq!(digest.as_bytes().len(), 32);
/// # }
/// ```
#[derive(Debug, Clone, Copy)]
pub enum Identity {}

impl<T: AsRef<[u8]>> Encode<T> for Identity {
    type Error = core::convert::Infallible;

    fn encode(value: &T) -> Result<Vec<u8>, Self::Error> {
        Ok(value.as_ref().to_vec())
    }
}

/// Error returned when decoding with Identity codec fails due to length mismatch.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IdentityDecodeError {
    /// The number of bytes expected.
    pub expected: usize,

    /// The number of bytes actually received.
    pub actual: usize,
}

impl core::fmt::Display for IdentityDecodeError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(
            f,
            "length mismatch: expected {} bytes, got {}",
            self.expected, self.actual
        )
    }
}

impl Decode<Vec<u8>> for Identity {
    type Error = core::convert::Infallible;

    fn decode(bytes: &[u8]) -> Result<Vec<u8>, Self::Error> {
        Ok(bytes.to_vec())
    }
}

impl<const N: usize> Decode<[u8; N]> for Identity {
    type Error = IdentityDecodeError;

    fn decode(bytes: &[u8]) -> Result<[u8; N], Self::Error> {
        bytes.try_into().map_err(|_| IdentityDecodeError {
            expected: N,
            actual: bytes.len(),
        })
    }
}

impl Canonical for Identity {}

/// A relaxed codec marker that tracks the underlying format without requiring encoding.
///
/// Use this for deserialized values where you know the codec but don't have
/// (or don't need) the encoder implementation.
///
/// # Example
///
/// ```
/// # #[cfg(feature = "sha2")]
/// # {
/// use evidence::digest::{Digest, DigestUnchecked, sha2::Sha256};
/// use evidence::codec::Relaxed;
/// use hybrid_array::Array;
///
/// // You received a digest over the network, encoded as CBOR
/// enum Cbor {}
///
/// let bytes: Array<u8, _> = Array::try_from([0u8; 32].as_slice()).unwrap();
///
/// // Use Relaxed<Cbor> to track the format without needing Encode impl
/// let digest: Digest<String, Sha256, Relaxed<Cbor>> = Digest::from_unchecked_array(bytes);
///
/// // This would NOT compile — Relaxed<Cbor> doesn't implement Encode:
/// // let d: Digest<String, Sha256, Relaxed<Cbor>> = Digest::hash(&my_string);
/// # }
/// ```
#[derive(Debug)]
pub struct Relaxed<C>(core::marker::PhantomData<C>);