use crate::amf0::type_marker::TypeMarker;
use crate::errors::AmfError;
use crate::traits::{Marshall, MarshallLength, Unmarshall};
use std::fmt::{Display, Formatter};
use std::ops::Deref;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct BooleanType {
type_marker: TypeMarker,
value: bool,
}
impl BooleanType {
pub fn new(value: bool) -> Self {
Self {
type_marker: TypeMarker::Boolean,
value,
}
}
}
impl Marshall for BooleanType {
fn marshall(&self) -> Result<Vec<u8>, AmfError> {
debug_assert!(self.type_marker == TypeMarker::Boolean);
let mut buf = [0u8; 2];
buf[0] = self.type_marker as u8; buf[1] = self.value as u8;
Ok(buf.to_vec())
}
}
impl MarshallLength for BooleanType {
fn marshall_length(&self) -> usize {
2 }
}
impl Unmarshall for BooleanType {
fn unmarshall(buf: &[u8]) -> Result<(Self, usize), AmfError> {
if buf.len() < 2 {
return Err(AmfError::BufferTooSmall {
want: 2,
got: buf.len(),
});
}
let type_marker = TypeMarker::try_from(buf[0])?; if type_marker != TypeMarker::Boolean {
return Err(AmfError::TypeMarkerValueMismatch {
want: TypeMarker::Boolean as u8,
got: buf[0],
});
}
let value = buf[1] != 0;
Ok((Self { type_marker, value }, 2))
}
}
impl TryFrom<&[u8]> for BooleanType {
type Error = AmfError;
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
Self::unmarshall(buf).map(|(b, _)| b)
}
}
impl TryFrom<Vec<u8>> for BooleanType {
type Error = AmfError;
fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
Self::try_from(vec.as_slice())
}
}
impl TryFrom<BooleanType> for Vec<u8> {
type Error = AmfError;
fn try_from(value: BooleanType) -> Result<Self, Self::Error> {
value.marshall()
}
}
impl From<bool> for BooleanType {
fn from(value: bool) -> Self {
Self::new(value)
}
}
impl From<BooleanType> for bool {
fn from(value: BooleanType) -> Self {
value.value
}
}
impl AsRef<bool> for BooleanType {
fn as_ref(&self) -> &bool {
&self.value
}
}
impl Deref for BooleanType {
type Target = bool;
fn deref(&self) -> &bool {
self.as_ref()
}
}
impl Display for BooleanType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value)
}
}
impl Default for BooleanType {
fn default() -> Self {
Self::new(false)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::amf0::type_marker::TypeMarker;
use crate::errors::AmfError;
use std::convert::TryFrom;
use std::fmt::Write as _;
use std::hash::{DefaultHasher, Hash, Hasher};
#[test]
fn boolean_round_trip_true() {
let orig = BooleanType::new(true);
let bytes = orig.marshall().expect("marshall should succeed");
assert_eq!(bytes, vec![TypeMarker::Boolean as u8, 1]);
let (decoded, len) = BooleanType::unmarshall(&bytes).expect("unmarshall should succeed");
assert_eq!(len, 2);
assert_eq!(decoded.value, true);
let from_buf = BooleanType::try_from(&bytes[..]).unwrap();
assert_eq!(from_buf.value, true);
let from_bool: BooleanType = false.into();
assert_eq!(from_bool.value, false);
assert_eq!(orig.as_ref(), &true);
assert_eq!(*orig, true);
let mut s = String::new();
write!(&mut s, "{}", orig).unwrap();
assert_eq!(s, "true");
}
#[test]
fn boolean_round_trip_false() {
let orig = BooleanType::new(false);
let bytes = orig.marshall().unwrap();
assert_eq!(bytes, vec![TypeMarker::Boolean as u8, 0]);
let (decoded, _) = BooleanType::unmarshall(&bytes).unwrap();
assert!(!decoded.value);
}
#[test]
fn boolean_unmarshall_errors() {
let err = BooleanType::unmarshall(&[TypeMarker::Boolean as u8]).unwrap_err();
match err {
AmfError::BufferTooSmall { want, got } => {
assert_eq!(want, 2);
assert_eq!(got, 1);
}
_ => panic!("expected BufferTooSmall"),
}
let bad = vec![TypeMarker::Number as u8, 1];
let err2 = BooleanType::unmarshall(&bad).unwrap_err();
match err2 {
AmfError::TypeMarkerValueMismatch { want, got } => {
assert_eq!(want, TypeMarker::Boolean as u8);
assert_eq!(got, TypeMarker::Number as u8);
}
_ => panic!("expected TypeMarkerValueMismatch"),
}
}
fn calculate_hash<T: Hash>(t: &T) -> u64 {
let mut hasher = DefaultHasher::new();
t.hash(&mut hasher);
hasher.finish()
}
#[test]
fn clone_preserves_equality() {
let orig = BooleanType::new(true);
let cloned = orig.clone();
assert_eq!(orig, cloned);
}
#[test]
fn eq_and_neq_behaviour() {
let a = BooleanType::new(true);
let b = BooleanType::new(true);
let c = BooleanType::new(false);
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn equal_values_have_same_hash() {
let x = BooleanType::new(true);
let y = BooleanType::new(true);
assert_eq!(calculate_hash(&x), calculate_hash(&y));
}
#[test]
fn different_values_have_different_hash() {
let x = BooleanType::new(true);
let y = BooleanType::new(false);
assert_ne!(calculate_hash(&x), calculate_hash(&y));
}
#[test]
fn clone_preserves_hash() {
let orig = BooleanType::new(false);
let cloned = orig.clone();
assert_eq!(calculate_hash(&orig), calculate_hash(&cloned));
}
}