mod counted;
mod primitives;
use thiserror::Error;
pub use counted::{
decode_bytes_u8, decode_bytes_u32, decode_counted_u8, decode_counted_u32, encode_bytes_u8,
encode_bytes_u32, encode_counted_u8, encode_counted_u32,
};
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum EncodeError {
#[error("string of {length} bytes exceeds the u8 length prefix limit of 255")]
StringTooLong { length: usize },
#[error("collection of {length} elements exceeds its count prefix limit of {limit}")]
CountOverflow { length: usize, limit: u64 },
#[error("byte string of {length} bytes exceeds its length prefix limit of {limit}")]
BytesOverflow { length: usize, limit: u64 },
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum DecodeError {
#[error("payload ended before the value was complete")]
UnexpectedEof,
#[error("string is not valid UTF-8")]
InvalidUtf8(#[from] std::string::FromUtf8Error),
#[error("invalid IP version tag {0}, expected 4 or 6")]
InvalidIpVersion(u8),
#[error("invalid NFC UID length {0}, must be 4, 7 or 10")]
InvalidNfcUidLength(u8),
#[error("hash must not be empty")]
EmptyHash,
#[error("{remaining} trailing bytes after the payload was decoded")]
TrailingBytes { remaining: usize },
}
pub trait Encode {
fn encode(&self, out: &mut Vec<u8>) -> Result<(), EncodeError>;
}
pub trait Decode: Sized {
fn decode(input: &mut &[u8]) -> Result<Self, DecodeError>;
}
pub fn encode_payload<T: Encode>(value: &T) -> Result<Vec<u8>, EncodeError> {
let mut out = Vec::new();
value.encode(&mut out)?;
Ok(out)
}
pub fn decode_payload<T: Decode>(payload: &[u8]) -> Result<T, DecodeError> {
let mut input = payload;
let value = T::decode(&mut input)?;
if !input.is_empty() {
return Err(DecodeError::TrailingBytes {
remaining: input.len(),
});
}
Ok(value)
}
pub(crate) fn take<'a>(input: &mut &'a [u8], length: usize) -> Result<&'a [u8], DecodeError> {
if input.len() < length {
return Err(DecodeError::UnexpectedEof);
}
let (taken, rest) = input.split_at(length);
*input = rest;
Ok(taken)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decode_payload_rejects_trailing_bytes() {
let payload = [7u8, 0xff];
let error = decode_payload::<u8>(&payload).unwrap_err();
assert!(matches!(error, DecodeError::TrailingBytes { remaining: 1 }));
}
#[test]
fn decode_payload_accepts_exact_payload() {
let payload = [7u8];
assert_eq!(decode_payload::<u8>(&payload).unwrap(), 7);
}
}