amf_rs/amf0/
boolean.rs

1use crate::amf0::type_marker::TypeMarker;
2use crate::errors::AmfError;
3use crate::traits::{Marshall, MarshallLength, Unmarshall};
4use std::fmt::{Display, Formatter};
5use std::ops::Deref;
6
7//	An AMF 0 Boolean type is used to encode a primitive ActionScript 1.0 or 2.0 Boolean or
8//	an ActionScript 3.0 Boolean. The Object (non-primitive) version of ActionScript 1.0 or
9//	2.0 Booleans are not serializable. A Boolean type marker is followed by an unsigned
10//	byte; a zero byte value denotes false while a non-zero byte value (typically 1) denotes
11//	true.
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub struct BooleanType {
14    type_marker: TypeMarker,
15    value: bool,
16}
17
18impl BooleanType {
19    pub fn new(value: bool) -> Self {
20        Self {
21            type_marker: TypeMarker::Boolean,
22            value,
23        }
24    }
25}
26
27impl Marshall for BooleanType {
28    fn marshall(&self) -> Result<Vec<u8>, AmfError> {
29        debug_assert!(self.type_marker == TypeMarker::Boolean);
30        let mut buf = [0u8; 2];
31        buf[0] = self.type_marker as u8; // 单字节情况下不用考虑字节序
32        buf[1] = self.value as u8;
33        Ok(buf.to_vec())
34    }
35}
36
37impl MarshallLength for BooleanType {
38    fn marshall_length(&self) -> usize {
39        2 // 1 byte for type marker + 1 byte for value
40    }
41}
42
43impl Unmarshall for BooleanType {
44    fn unmarshall(buf: &[u8]) -> Result<(Self, usize), AmfError> {
45        if buf.len() < 2 {
46            return Err(AmfError::BufferTooSmall {
47                want: 2,
48                got: buf.len(),
49            });
50        }
51        let type_marker = TypeMarker::try_from(buf[0])?; // 这里直接用了 buf[0] 是应为单字节情况下不用考虑字节序
52        if type_marker != TypeMarker::Boolean {
53            return Err(AmfError::TypeMarkerValueMismatch {
54                want: TypeMarker::Boolean as u8,
55                got: buf[0],
56            });
57        }
58        let value = buf[1] != 0;
59        Ok((Self { type_marker, value }, 2))
60    }
61}
62
63// 实现 rust 惯用语("idiom") 方便用户使用
64
65impl TryFrom<&[u8]> for BooleanType {
66    type Error = AmfError;
67
68    fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
69        Self::unmarshall(buf).map(|(b, _)| b)
70    }
71}
72
73impl TryFrom<Vec<u8>> for BooleanType {
74    type Error = AmfError;
75
76    fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
77        Self::try_from(vec.as_slice())
78    }
79}
80
81impl TryFrom<BooleanType> for Vec<u8> {
82    type Error = AmfError;
83
84    fn try_from(value: BooleanType) -> Result<Self, Self::Error> {
85        value.marshall()
86    }
87}
88
89impl From<bool> for BooleanType {
90    fn from(value: bool) -> Self {
91        Self::new(value)
92    }
93}
94
95impl From<BooleanType> for bool {
96    fn from(value: BooleanType) -> Self {
97        value.value
98    }
99}
100
101impl AsRef<bool> for BooleanType {
102    fn as_ref(&self) -> &bool {
103        &self.value
104    }
105}
106
107impl Deref for BooleanType {
108    type Target = bool;
109
110    fn deref(&self) -> &bool {
111        self.as_ref()
112    }
113}
114
115impl Display for BooleanType {
116    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
117        write!(f, "{}", self.value)
118    }
119}
120
121impl Default for BooleanType {
122    fn default() -> Self {
123        Self::new(false)
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130    use crate::amf0::type_marker::TypeMarker;
131    use crate::errors::AmfError;
132    use std::convert::TryFrom;
133    use std::fmt::Write as _;
134    use std::hash::{DefaultHasher, Hash, Hasher};
135    // for Display tests
136
137    #[test]
138    fn boolean_round_trip_true() {
139        let orig = BooleanType::new(true);
140        let bytes = orig.marshall().expect("marshall should succeed");
141        // [marker, value]
142        assert_eq!(bytes, vec![TypeMarker::Boolean as u8, 1]);
143        // unmarshall
144        let (decoded, len) = BooleanType::unmarshall(&bytes).expect("unmarshall should succeed");
145        assert_eq!(len, 2);
146        assert_eq!(decoded.value, true);
147        // TryFrom
148        let from_buf = BooleanType::try_from(&bytes[..]).unwrap();
149        assert_eq!(from_buf.value, true);
150        // From<bool>
151        let from_bool: BooleanType = false.into();
152        assert_eq!(from_bool.value, false);
153        // AsRef, Deref
154        assert_eq!(orig.as_ref(), &true);
155        assert_eq!(*orig, true);
156        // Display
157        let mut s = String::new();
158        write!(&mut s, "{}", orig).unwrap();
159        assert_eq!(s, "true");
160    }
161
162    #[test]
163    fn boolean_round_trip_false() {
164        let orig = BooleanType::new(false);
165        let bytes = orig.marshall().unwrap();
166        assert_eq!(bytes, vec![TypeMarker::Boolean as u8, 0]);
167        let (decoded, _) = BooleanType::unmarshall(&bytes).unwrap();
168        assert!(!decoded.value);
169    }
170
171    #[test]
172    fn boolean_unmarshall_errors() {
173        // too short
174        let err = BooleanType::unmarshall(&[TypeMarker::Boolean as u8]).unwrap_err();
175        match err {
176            AmfError::BufferTooSmall { want, got } => {
177                assert_eq!(want, 2);
178                assert_eq!(got, 1);
179            }
180            _ => panic!("expected BufferTooSmall"),
181        }
182        // wrong marker
183        let bad = vec![TypeMarker::Number as u8, 1];
184        let err2 = BooleanType::unmarshall(&bad).unwrap_err();
185        match err2 {
186            AmfError::TypeMarkerValueMismatch { want, got } => {
187                assert_eq!(want, TypeMarker::Boolean as u8);
188                assert_eq!(got, TypeMarker::Number as u8);
189            }
190            _ => panic!("expected TypeMarkerValueMismatch"),
191        }
192    }
193
194    fn calculate_hash<T: Hash>(t: &T) -> u64 {
195        let mut hasher = DefaultHasher::new();
196        t.hash(&mut hasher);
197        hasher.finish()
198    }
199
200    #[test]
201    fn clone_preserves_equality() {
202        let orig = BooleanType::new(true);
203        let cloned = orig.clone();
204        assert_eq!(orig, cloned);
205    }
206
207    #[test]
208    fn eq_and_neq_behaviour() {
209        let a = BooleanType::new(true);
210        let b = BooleanType::new(true);
211        let c = BooleanType::new(false);
212
213        assert_eq!(a, b);
214        assert_ne!(a, c);
215    }
216
217    #[test]
218    fn equal_values_have_same_hash() {
219        let x = BooleanType::new(true);
220        let y = BooleanType::new(true);
221
222        assert_eq!(calculate_hash(&x), calculate_hash(&y));
223    }
224
225    #[test]
226    fn different_values_have_different_hash() {
227        let x = BooleanType::new(true);
228        let y = BooleanType::new(false);
229
230        assert_ne!(calculate_hash(&x), calculate_hash(&y));
231    }
232
233    #[test]
234    fn clone_preserves_hash() {
235        let orig = BooleanType::new(false);
236        let cloned = orig.clone();
237
238        assert_eq!(calculate_hash(&orig), calculate_hash(&cloned));
239    }
240}