use crate::amf0::type_marker::TypeMarker;
use crate::errors::AmfError;
use crate::traits::{Marshall, MarshallLength, Unmarshall};
use std::fmt::Display;
use std::hash::{Hash, Hasher};
pub trait MarkerType: Sized {
const TM: TypeMarker;
}
impl<M: MarkerType> Marshall for M {
fn marshall(&self) -> Result<Vec<u8>, AmfError> {
let mut buf = [0u8; 1];
buf[0] = M::TM as u8; Ok(buf.to_vec())
}
}
impl<M: MarkerType> MarshallLength for M {
fn marshall_length(&self) -> usize {
1
}
}
impl<M: MarkerType + Default> Unmarshall for M {
fn unmarshall(buf: &[u8]) -> Result<(Self, usize), AmfError> {
if buf.len() < 1 {
return Err(AmfError::BufferTooSmall {
want: 1,
got: buf.len(),
});
}
let type_marker = TypeMarker::try_from(buf[0])?;
if type_marker != M::TM {
return Err(AmfError::TypeMarkerValueMismatch {
want: M::TM as u8,
got: buf[0],
});
}
Ok((M::default(), 1))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct NullType;
impl MarkerType for NullType {
const TM: TypeMarker = TypeMarker::Null;
}
impl TryFrom<&[u8]> for NullType {
type Error = AmfError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Self::unmarshall(value).map(|(o, _)| o)
}
}
impl TryFrom<Vec<u8>> for NullType {
type Error = AmfError;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
Self::try_from(value.as_slice())
}
}
impl TryFrom<NullType> for Vec<u8> {
type Error = AmfError;
fn try_from(value: NullType) -> Result<Self, Self::Error> {
value.marshall()
}
}
impl Display for NullType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "null")
}
}
impl Hash for NullType {
fn hash<H: Hasher>(&self, state: &mut H) {
TypeMarker::Null.hash(state);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct UndefinedType;
impl MarkerType for UndefinedType {
const TM: TypeMarker = TypeMarker::Undefined;
}
impl TryFrom<&[u8]> for UndefinedType {
type Error = AmfError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Self::unmarshall(value).map(|(o, _)| o)
}
}
impl TryFrom<Vec<u8>> for UndefinedType {
type Error = AmfError;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
Self::try_from(value.as_slice())
}
}
impl TryFrom<UndefinedType> for Vec<u8> {
type Error = AmfError;
fn try_from(value: UndefinedType) -> Result<Self, Self::Error> {
value.marshall()
}
}
impl Display for UndefinedType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "undefined")
}
}
impl Hash for UndefinedType {
fn hash<H: Hasher>(&self, state: &mut H) {
TypeMarker::Undefined.hash(state);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::amf0::type_marker::TypeMarker;
use std::hash::{DefaultHasher, Hash, Hasher};
#[test]
fn test_null_marshall() {
let null = NullType;
let data = null.marshall().unwrap();
assert_eq!(data, vec![TypeMarker::Null as u8]);
}
#[test]
fn test_null_marshall_length() {
let null = NullType;
assert_eq!(null.marshall_length(), 1);
}
#[test]
fn test_null_unmarshall_valid() {
let data = [TypeMarker::Null as u8];
let (null, bytes_read) = NullType::unmarshall(&data).unwrap();
assert_eq!(bytes_read, 1);
assert_eq!(null, NullType);
}
#[test]
fn test_null_unmarshall_buffer_too_small() {
let data = [];
let result = NullType::unmarshall(&data);
assert!(matches!(
result,
Err(AmfError::BufferTooSmall { want: 1, got: 0 })
));
}
#[test]
fn test_null_try_from() {
let data = [TypeMarker::Null as u8];
let null = NullType::try_from(&data[..]).unwrap();
assert_eq!(null, NullType);
}
#[test]
fn test_null_display() {
assert_eq!(format!("{}", NullType), "null");
}
#[test]
fn test_undefined_marshall() {
let undefined = UndefinedType;
let data = undefined.marshall().unwrap();
assert_eq!(data, vec![TypeMarker::Undefined as u8]);
}
#[test]
fn test_undefined_marshall_length() {
let undefined = UndefinedType;
assert_eq!(undefined.marshall_length(), 1);
}
#[test]
fn test_undefined_unmarshall_valid() {
let data = [TypeMarker::Undefined as u8];
let (undefined, bytes_read) = UndefinedType::unmarshall(&data).unwrap();
assert_eq!(bytes_read, 1);
assert_eq!(undefined, UndefinedType);
}
#[test]
fn test_undefined_unmarshall_buffer_too_small() {
let data = [];
let result = UndefinedType::unmarshall(&data);
assert!(matches!(
result,
Err(AmfError::BufferTooSmall { want: 1, got: 0 })
));
}
#[test]
fn test_undefined_try_from() {
let data = [TypeMarker::Undefined as u8];
let undefined = UndefinedType::try_from(&data[..]).unwrap();
assert_eq!(undefined, UndefinedType);
}
#[test]
fn test_undefined_display() {
assert_eq!(format!("{}", UndefinedType), "undefined");
}
#[test]
fn test_generic_marker_type() {
assert_eq!(NullType::TM, TypeMarker::Null);
assert_eq!(UndefinedType::TM, TypeMarker::Undefined);
}
fn calculate_hash<T: Hash>(t: &T) -> u64 {
let mut hasher = DefaultHasher::new();
t.hash(&mut hasher);
hasher.finish()
}
#[test]
fn null_and_undefined_clone_eq_hash() {
let n1 = NullType::default(); let n2 = n1.clone();
assert_eq!(n1, n2);
let u1 = UndefinedType::default();
let u2 = u1.clone();
assert_eq!(u1, u2);
assert_eq!(calculate_hash(&n1), calculate_hash(&n2));
assert_eq!(calculate_hash(&u1), calculate_hash(&u2));
assert_ne!(calculate_hash(&n1), calculate_hash(&u1));
}
}