scuffle_amf0/
define.rs

1use std::borrow::Cow;
2
3use num_derive::FromPrimitive;
4
5/// AMF0 marker types.
6/// Defined in amf0_spec_121207.pdf section 2.1
7#[derive(Debug, PartialEq, Eq, Clone, Copy, FromPrimitive)]
8#[repr(u8)]
9pub enum Amf0Marker {
10    Number = 0x00,
11    Boolean = 0x01,
12    String = 0x02,
13    Object = 0x03,
14    MovieClipMarker = 0x04, // reserved, not supported
15    Null = 0x05,
16    Undefined = 0x06,
17    Reference = 0x07,
18    EcmaArray = 0x08,
19    ObjectEnd = 0x09,
20    StrictArray = 0x0a,
21    Date = 0x0b,
22    LongString = 0x0c,
23    Unsupported = 0x0d,
24    Recordset = 0x0e, // reserved, not supported
25    XmlDocument = 0x0f,
26    TypedObject = 0x10,
27    AVMPlusObject = 0x11, // AMF3 marker
28}
29
30/// AMF0 value types.
31/// Defined in amf0_spec_121207.pdf section 2.2-2.14
32#[derive(PartialEq, Clone, Debug)]
33pub enum Amf0Value<'a> {
34    /// Number Type defined section 2.2
35    Number(f64),
36    /// Boolean Type defined section 2.3
37    Boolean(bool),
38    /// String Type defined section 2.4
39    String(Cow<'a, str>),
40    /// Object Type defined section 2.5
41    Object(Cow<'a, [(Cow<'a, str>, Amf0Value<'a>)]>),
42    /// Null Type defined section 2.7
43    Null,
44    /// Undefined Type defined section 2.8
45    ObjectEnd,
46    /// LongString Type defined section 2.14
47    LongString(Cow<'a, str>),
48}
49
50impl Amf0Value<'_> {
51    /// Get the marker of the value.
52    pub fn marker(&self) -> Amf0Marker {
53        match self {
54            Self::Boolean(_) => Amf0Marker::Boolean,
55            Self::Number(_) => Amf0Marker::Number,
56            Self::String(_) => Amf0Marker::String,
57            Self::Object(_) => Amf0Marker::Object,
58            Self::Null => Amf0Marker::Null,
59            Self::ObjectEnd => Amf0Marker::ObjectEnd,
60            Self::LongString(_) => Amf0Marker::LongString,
61        }
62    }
63
64    /// Get the owned value.
65    pub fn to_owned(&self) -> Amf0Value<'static> {
66        match self {
67            Self::String(s) => Amf0Value::String(Cow::Owned(s.to_string())),
68            Self::LongString(s) => Amf0Value::LongString(Cow::Owned(s.to_string())),
69            Self::Object(o) => Amf0Value::Object(o.iter().map(|(k, v)| (Cow::Owned(k.to_string()), v.to_owned())).collect()),
70            Self::Number(n) => Amf0Value::Number(*n),
71            Self::Boolean(b) => Amf0Value::Boolean(*b),
72            Self::Null => Amf0Value::Null,
73            Self::ObjectEnd => Amf0Value::ObjectEnd,
74        }
75    }
76}
77
78#[cfg(test)]
79#[cfg_attr(all(test, coverage_nightly), coverage(off))]
80mod tests {
81    use num_traits::FromPrimitive;
82
83    use super::*;
84
85    #[test]
86    fn test_marker() {
87        let cases = [
88            (Amf0Value::Number(1.0), Amf0Marker::Number),
89            (Amf0Value::Boolean(true), Amf0Marker::Boolean),
90            (Amf0Value::String(Cow::Borrowed("test")), Amf0Marker::String),
91            (
92                Amf0Value::Object(Cow::Borrowed(&[(Cow::Borrowed("test"), Amf0Value::Number(1.0))])),
93                Amf0Marker::Object,
94            ),
95            (Amf0Value::Null, Amf0Marker::Null),
96            (Amf0Value::ObjectEnd, Amf0Marker::ObjectEnd),
97            (Amf0Value::LongString(Cow::Borrowed("test")), Amf0Marker::LongString),
98        ];
99
100        for (value, marker) in cases {
101            assert_eq!(value.marker(), marker);
102        }
103    }
104
105    #[test]
106    fn test_to_owned() {
107        let value = Amf0Value::Object(Cow::Borrowed(&[(
108            Cow::Borrowed("test"),
109            Amf0Value::LongString(Cow::Borrowed("test")),
110        )]));
111        let owned = value.to_owned();
112        assert_eq!(
113            owned,
114            Amf0Value::Object(Cow::Owned(vec![(
115                "test".to_string().into(),
116                Amf0Value::LongString(Cow::Owned("test".to_string()))
117            )]))
118        );
119
120        let value = Amf0Value::String(Cow::Borrowed("test"));
121        let owned = value.to_owned();
122        assert_eq!(owned, Amf0Value::String(Cow::Owned("test".to_string())));
123
124        let value = Amf0Value::Number(1.0);
125        let owned = value.to_owned();
126        assert_eq!(owned, Amf0Value::Number(1.0));
127
128        let value = Amf0Value::Boolean(true);
129        let owned = value.to_owned();
130        assert_eq!(owned, Amf0Value::Boolean(true));
131
132        let value = Amf0Value::Null;
133        let owned = value.to_owned();
134        assert_eq!(owned, Amf0Value::Null);
135
136        let value = Amf0Value::ObjectEnd;
137        let owned = value.to_owned();
138        assert_eq!(owned, Amf0Value::ObjectEnd);
139    }
140
141    #[test]
142    fn test_marker_primitive() {
143        let cases = [
144            (Amf0Marker::Number, 0x00),
145            (Amf0Marker::Boolean, 0x01),
146            (Amf0Marker::String, 0x02),
147            (Amf0Marker::Object, 0x03),
148            (Amf0Marker::MovieClipMarker, 0x04),
149            (Amf0Marker::Null, 0x05),
150            (Amf0Marker::Undefined, 0x06),
151            (Amf0Marker::Reference, 0x07),
152            (Amf0Marker::EcmaArray, 0x08),
153            (Amf0Marker::ObjectEnd, 0x09),
154            (Amf0Marker::StrictArray, 0x0a),
155            (Amf0Marker::Date, 0x0b),
156            (Amf0Marker::LongString, 0x0c),
157            (Amf0Marker::Unsupported, 0x0d),
158            (Amf0Marker::Recordset, 0x0e),
159            (Amf0Marker::XmlDocument, 0x0f),
160            (Amf0Marker::TypedObject, 0x10),
161            (Amf0Marker::AVMPlusObject, 0x11),
162        ];
163
164        for (marker, value) in cases {
165            assert_eq!(marker as u8, value);
166            assert_eq!(Amf0Marker::from_u8(value), Some(marker));
167        }
168
169        assert!(Amf0Marker::from_u8(0x12).is_none());
170    }
171}