1use crate::amf0::boolean::BooleanType;
2use crate::amf0::marker::{NullType, UndefinedType};
3use crate::amf0::number::NumberType;
4use crate::amf0::object_end::ObjectEndType;
5use crate::amf0::string::{LongStringType, StringType};
6use crate::amf0::type_marker::TypeMarker;
7use crate::amf0::unsupported::{
8 DateType, MovieClipType, RecordsetType, ReferenceType, StrictArrayType, TypedObjectType,
9 UnsupportedType, XmlDocumentType,
10};
11use crate::amf0::utf8::Utf8;
12use crate::errors::AmfError;
13use crate::traits::{Marshall, MarshallLength, Unmarshall};
14use indexmap::IndexMap;
15use std::borrow::Borrow;
16use std::fmt::Display;
17use std::io;
18use std::ops::Deref;
19
20#[derive(Debug, Clone, PartialEq)]
21pub enum Amf0TypedValue {
22 Number(NumberType),
23 Boolean(BooleanType),
24 String(StringType),
25 Object(ObjectType),
26 MovieClip(MovieClipType),
27 Null(NullType),
28 Undefined(UndefinedType),
29 Reference(ReferenceType),
30 EcmaArray(EcmaArrayType),
31 ObjectEnd(ObjectEndType),
32 StrictArray(StrictArrayType),
33 Date(DateType),
34 LongString(LongStringType),
35 Unsupported(UnsupportedType),
36 Recordset(RecordsetType),
37 XmlDocument(XmlDocumentType),
38 TypedObject(TypedObjectType),
39}
40
41impl Marshall for Amf0TypedValue {
42 fn marshall(&self) -> Result<Vec<u8>, AmfError> {
43 match self {
44 Amf0TypedValue::Number(v) => v.marshall(),
45 Amf0TypedValue::Boolean(v) => v.marshall(),
46 Amf0TypedValue::String(v) => v.marshall(),
47 Amf0TypedValue::Object(v) => v.marshall(),
48 Amf0TypedValue::MovieClip(v) => v.marshall(),
49 Amf0TypedValue::Null(v) => v.marshall(),
50 Amf0TypedValue::Undefined(v) => v.marshall(),
51 Amf0TypedValue::Reference(v) => v.marshall(),
52 Amf0TypedValue::EcmaArray(v) => v.marshall(),
53 Amf0TypedValue::ObjectEnd(v) => v.marshall(),
54 Amf0TypedValue::StrictArray(v) => v.marshall(),
55 Amf0TypedValue::Date(v) => v.marshall(),
56 Amf0TypedValue::LongString(v) => v.marshall(),
57 Amf0TypedValue::Unsupported(v) => v.marshall(),
58 Amf0TypedValue::Recordset(v) => v.marshall(),
59 Amf0TypedValue::XmlDocument(v) => v.marshall(),
60 Amf0TypedValue::TypedObject(v) => v.marshall(),
61 }
62 }
63}
64
65impl MarshallLength for Amf0TypedValue {
66 fn marshall_length(&self) -> usize {
67 match self {
68 Amf0TypedValue::Number(v) => v.marshall_length(),
69 Amf0TypedValue::Boolean(v) => v.marshall_length(),
70 Amf0TypedValue::String(v) => v.marshall_length(),
71 Amf0TypedValue::Object(v) => v.marshall_length(),
72 Amf0TypedValue::MovieClip(v) => v.marshall_length(),
73 Amf0TypedValue::Null(v) => v.marshall_length(),
74 Amf0TypedValue::Undefined(v) => v.marshall_length(),
75 Amf0TypedValue::Reference(v) => v.marshall_length(),
76 Amf0TypedValue::EcmaArray(v) => v.marshall_length(),
77 Amf0TypedValue::ObjectEnd(v) => v.marshall_length(),
78 Amf0TypedValue::StrictArray(v) => v.marshall_length(),
79 Amf0TypedValue::Date(v) => v.marshall_length(),
80 Amf0TypedValue::LongString(v) => v.marshall_length(),
81 Amf0TypedValue::Unsupported(v) => v.marshall_length(),
82 Amf0TypedValue::Recordset(v) => v.marshall_length(),
83 Amf0TypedValue::XmlDocument(v) => v.marshall_length(),
84 Amf0TypedValue::TypedObject(v) => v.marshall_length(),
85 }
86 }
87}
88
89impl Unmarshall for Amf0TypedValue {
90 fn unmarshall(buf: &[u8]) -> Result<(Self, usize), AmfError> {
91 if buf.is_empty() {
92 return Err(AmfError::Custom("Buffer is empty".to_string()));
93 }
94 if buf.len() >= 3 && buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x09 {
95 return Ok((Amf0TypedValue::ObjectEnd(ObjectEndType::default()), 3));
96 }
97
98 let type_marker = TypeMarker::try_from(buf[0])?;
99 match type_marker {
100 TypeMarker::Number => {
101 NumberType::unmarshall(buf).map(|v| (Amf0TypedValue::Number(v.0), v.1))
102 }
103 TypeMarker::Boolean => {
104 BooleanType::unmarshall(buf).map(|v| (Amf0TypedValue::Boolean(v.0), v.1))
105 }
106 TypeMarker::String => {
107 StringType::unmarshall(buf).map(|v| (Amf0TypedValue::String(v.0), v.1))
108 }
109 TypeMarker::Object => {
110 ObjectType::unmarshall(buf).map(|v| (Amf0TypedValue::Object(v.0), v.1))
111 }
112 TypeMarker::MovieClip => {
113 MovieClipType::unmarshall(buf).map(|v| (Amf0TypedValue::MovieClip(v.0), v.1))
114 }
115 TypeMarker::Null => NullType::unmarshall(buf).map(|v| (Amf0TypedValue::Null(v.0), v.1)),
116 TypeMarker::Undefined => {
117 UndefinedType::unmarshall(buf).map(|v| (Amf0TypedValue::Undefined(v.0), v.1))
118 }
119 TypeMarker::Reference => {
120 ReferenceType::unmarshall(buf).map(|v| (Amf0TypedValue::Reference(v.0), v.1))
121 }
122 TypeMarker::EcmaArray => {
123 EcmaArrayType::unmarshall(buf).map(|v| (Amf0TypedValue::EcmaArray(v.0), v.1))
124 }
125 TypeMarker::ObjectEnd => {
126 panic!("cannot happen")
127 }
128 TypeMarker::StrictArray => {
129 StrictArrayType::unmarshall(buf).map(|v| (Amf0TypedValue::StrictArray(v.0), v.1))
130 }
131 TypeMarker::Date => DateType::unmarshall(buf).map(|v| (Amf0TypedValue::Date(v.0), v.1)),
132 TypeMarker::LongString => {
133 LongStringType::unmarshall(buf).map(|v| (Amf0TypedValue::LongString(v.0), v.1))
134 }
135 TypeMarker::Unsupported => {
136 UnsupportedType::unmarshall(buf).map(|v| (Amf0TypedValue::Unsupported(v.0), v.1))
137 }
138 TypeMarker::Recordset => {
139 RecordsetType::unmarshall(buf).map(|v| (Amf0TypedValue::Recordset(v.0), v.1))
140 }
141 TypeMarker::XmlDocument => {
142 XmlDocumentType::unmarshall(buf).map(|v| (Amf0TypedValue::XmlDocument(v.0), v.1))
143 }
144 TypeMarker::TypedObject => {
145 TypedObjectType::unmarshall(buf).map(|v| (Amf0TypedValue::TypedObject(v.0), v.1))
146 }
147 }
148 }
149}
150
151impl TryFrom<&[u8]> for Amf0TypedValue {
152 type Error = AmfError;
153
154 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
155 Self::unmarshall(value).map(|(o, _)| o)
156 }
157}
158
159impl TryFrom<Vec<u8>> for Amf0TypedValue {
160 type Error = AmfError;
161
162 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
163 Self::try_from(value.as_slice())
164 }
165}
166
167impl TryFrom<Amf0TypedValue> for Vec<u8> {
168 type Error = AmfError;
169
170 fn try_from(value: Amf0TypedValue) -> Result<Self, Self::Error> {
171 value.marshall()
172 }
173}
174
175impl Display for Amf0TypedValue {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 match self {
178 Amf0TypedValue::Number(v) => v.fmt(f),
179 Amf0TypedValue::Boolean(v) => v.fmt(f),
180 Amf0TypedValue::String(v) => v.fmt(f),
181 Amf0TypedValue::Object(v) => v.fmt(f),
182 Amf0TypedValue::MovieClip(v) => v.fmt(f),
183 Amf0TypedValue::Null(v) => v.fmt(f),
184 Amf0TypedValue::Undefined(v) => v.fmt(f),
185 Amf0TypedValue::Reference(v) => v.fmt(f),
186 Amf0TypedValue::EcmaArray(v) => v.fmt(f),
187 Amf0TypedValue::ObjectEnd(v) => v.fmt(f),
188 Amf0TypedValue::StrictArray(v) => v.fmt(f),
189 Amf0TypedValue::Date(v) => v.fmt(f),
190 Amf0TypedValue::LongString(v) => v.fmt(f),
191 Amf0TypedValue::Unsupported(v) => v.fmt(f),
192 Amf0TypedValue::Recordset(v) => v.fmt(f),
193 Amf0TypedValue::XmlDocument(v) => v.fmt(f),
194 Amf0TypedValue::TypedObject(v) => v.fmt(f),
195 }
196 }
197}
198
199#[derive(Debug, Clone, PartialEq)]
200pub struct NestedType<const LBW: usize, const TM: u8> {
201 length: Option<u32>,
202 properties: IndexMap<Utf8, Amf0TypedValue>,
203 object_end: ObjectEndType,
204}
205
206impl<const LBW: usize, const TM: u8> NestedType<LBW, TM> {
207 pub fn new(properties: IndexMap<Utf8, Amf0TypedValue>) -> Self {
208 let length = if LBW == 4 {
209 Some(properties.len() as u32)
210 } else {
211 None
212 };
213 Self {
214 length,
215 properties,
216 object_end: ObjectEndType::default(),
217 }
218 }
219}
220
221impl<const LBW: usize, const TM: u8> Marshall for NestedType<LBW, TM> {
222 fn marshall(&self) -> Result<Vec<u8>, AmfError> {
223 let mut vec = Vec::with_capacity(self.marshall_length());
224 vec.push(TM);
225
226 if let Some(length) = self.length {
227 let length_bytes = length.to_be_bytes();
228 vec.extend_from_slice(&length_bytes);
229 }
230
231 self.properties
232 .iter()
233 .try_for_each(|(k, v)| -> io::Result<()> {
234 let k_vec = k
235 .marshall()
236 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
237 vec.extend_from_slice(&k_vec);
238 let v_vec = v
239 .marshall()
240 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
241 vec.extend_from_slice(&v_vec);
242 Ok(())
243 })?;
244
245 let object_end_vec = self.object_end.marshall()?;
246 vec.extend_from_slice(&object_end_vec);
247
248 Ok(vec)
249 }
250}
251
252impl<const LBW: usize, const TM: u8> MarshallLength for NestedType<LBW, TM> {
253 fn marshall_length(&self) -> usize {
254 let mut size = 1; size += LBW;
256 let properties_bytes_size: usize = self
257 .properties
258 .iter()
259 .map(|(k, v)| k.marshall_length() + v.marshall_length())
260 .sum();
261 size += properties_bytes_size;
262 size += self.object_end.marshall_length();
263 size
264 }
265}
266
267impl<const LBW: usize, const TM: u8> Unmarshall for NestedType<LBW, TM> {
268 fn unmarshall(buf: &[u8]) -> Result<(Self, usize), AmfError> {
269 let required_size = 1 + LBW + 3; if buf.len() < required_size {
271 return Err(AmfError::BufferTooSmall {
273 want: required_size,
274 got: buf.len(),
275 });
276 }
277
278 if buf[0] != TM {
279 return Err(AmfError::TypeMarkerValueMismatch {
280 want: TM,
281 got: buf[0],
282 });
283 }
284
285 let mut length = 0u32;
286 if LBW == 4 {
287 length = u32::from_be_bytes(
288 buf[1..1 + LBW]
289 .try_into()
290 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?,
291 );
292 }
293
294 let mut properties = IndexMap::new();
295 let mut offset = 1 + LBW;
296 while offset < buf.len() {
297 if offset <= buf.len() - 3 {
298 if buf[offset] == 0x00 && buf[offset + 1] == 0x00 && buf[offset + 2] == 0x09 {
300 break;
301 }
302 }
303
304 let (k, k_len) = Utf8::unmarshall(&buf[offset..])?;
305 offset += k_len;
306 let (v, v_len) = Amf0TypedValue::unmarshall(&buf[offset..])?;
307 offset += v_len;
308 properties.insert(k, v);
309 }
310
311 if buf[buf.len() - 3..] != [0x00, 0x00, 0x09] {
313 return Err(AmfError::Custom(
314 "Invalid object, expected object end, got end of buffer".to_string(),
315 ));
316 }
317
318 if LBW == 4 && properties.len() != length as usize {
320 return Err(AmfError::Custom(format!(
321 "Invalid properties length, want {}, got {}",
322 length,
323 properties.len()
324 )));
325 }
326
327 let read_size = if offset == buf.len() {
328 offset
329 } else if offset == buf.len() - 3 {
330 offset + 3
331 } else {
332 buf.len()
333 };
334 Ok((Self::new(properties), read_size))
335 }
336}
337
338impl<const LBW: usize, const TM: u8> TryFrom<&[u8]> for NestedType<LBW, TM> {
339 type Error = AmfError;
340
341 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
342 Self::unmarshall(value).map(|(v, _)| v)
343 }
344}
345
346impl<const LBW: usize, const TM: u8> TryFrom<Vec<u8>> for NestedType<LBW, TM> {
347 type Error = AmfError;
348
349 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
350 Self::try_from(value.as_slice())
351 }
352}
353
354impl<const LBW: usize, const TM: u8> TryFrom<NestedType<LBW, TM>> for Vec<u8> {
355 type Error = AmfError;
356
357 fn try_from(value: NestedType<LBW, TM>) -> Result<Self, Self::Error> {
358 value.marshall()
359 }
360}
361
362impl<K, V, const LBW: usize, const TM: u8> From<IndexMap<K, V>> for NestedType<LBW, TM>
363where
364 K: Into<Utf8>,
365 V: Into<Amf0TypedValue>,
366{
367 fn from(value: IndexMap<K, V>) -> Self {
368 let properties = value
369 .into_iter()
370 .map(|(k, v)| (k.into(), v.into()))
371 .collect();
372 Self::new(properties)
373 }
374}
375
376impl<const LBW: usize, const TM: u8> AsRef<IndexMap<Utf8, Amf0TypedValue>> for NestedType<LBW, TM> {
377 fn as_ref(&self) -> &IndexMap<Utf8, Amf0TypedValue> {
378 &self.properties
379 }
380}
381
382impl<const LBW: usize, const TM: u8> Deref for NestedType<LBW, TM> {
383 type Target = IndexMap<Utf8, Amf0TypedValue>;
384
385 fn deref(&self) -> &Self::Target {
386 self.as_ref()
387 }
388}
389
390impl<const LBW: usize, const TM: u8> Borrow<IndexMap<Utf8, Amf0TypedValue>>
391 for NestedType<LBW, TM>
392{
393 fn borrow(&self) -> &IndexMap<Utf8, Amf0TypedValue> {
394 self.as_ref()
395 }
396}
397
398impl<const LBW: usize, const TM: u8> Display for NestedType<LBW, TM> {
399 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
400 write!(f, "{{")?; let mut iter = self.properties.iter().peekable();
403 while let Some((key, value)) = iter.next() {
404 write!(f, "\"{}\":{}", key, value)?;
407 if iter.peek().is_some() {
409 write!(f, ",")?;
410 }
411 }
412 write!(f, "}}") }
414}
415
416impl<const LBW: usize, const TM: u8> Default for NestedType<LBW, TM> {
417 fn default() -> Self {
418 Self::new(IndexMap::new())
419 }
420}
421
422impl<K, V, const LBW: usize, const TM: u8> FromIterator<(K, V)> for NestedType<LBW, TM>
423where
424 K: Into<Utf8>,
425 V: Into<Amf0TypedValue>,
426{
427 fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
428 let properties = iter
429 .into_iter()
430 .map(|(k, v)| (k.into(), v.into()))
431 .collect();
432 Self::new(properties)
433 }
434}
435
436impl<const LBW: usize, const TM: u8> IntoIterator for NestedType<LBW, TM> {
437 type Item = (Utf8, Amf0TypedValue);
438 type IntoIter = indexmap::map::IntoIter<Utf8, Amf0TypedValue>;
439
440 fn into_iter(self) -> Self::IntoIter {
441 self.properties.into_iter()
442 }
443}
444
445pub type ObjectType = NestedType<0, { TypeMarker::Object as u8 }>;
452
453pub type EcmaArrayType = NestedType<4, { TypeMarker::EcmaArray as u8 }>;
458
459#[cfg(test)]
460mod tests {
461 use super::*;
462 use indexmap::IndexMap;
463
464 fn sample_properties() -> IndexMap<Utf8, Amf0TypedValue> {
466 let mut props = IndexMap::new();
467 props.insert(
468 Utf8::new_from_str("key1").unwrap(),
469 Amf0TypedValue::Number(NumberType::new(42.0)),
470 );
471 props.insert(
472 Utf8::new_from_str("key2").unwrap(),
473 Amf0TypedValue::String(StringType::try_from("value").unwrap()),
474 );
475 props
476 }
477
478 #[test]
480 fn test_number() {
481 let original = Amf0TypedValue::Number(NumberType::new(42.0));
482 let marshalled = original.marshall().unwrap();
483 let (unmarshalled, _) = Amf0TypedValue::unmarshall(&marshalled).unwrap();
484 assert_eq!(original, unmarshalled);
485 }
486
487 #[test]
488 fn test_boolean() {
489 let original = Amf0TypedValue::Boolean(BooleanType::new(true));
490 let marshalled = original.marshall().unwrap();
491 let (unmarshalled, _) = Amf0TypedValue::unmarshall(&marshalled).unwrap();
492 assert_eq!(original, unmarshalled);
493 }
494
495 #[test]
496 fn test_string() {
497 let original = Amf0TypedValue::String(StringType::new_from_str("hello").unwrap());
498 let marshalled = original.marshall().unwrap();
499 let (unmarshalled, _) = Amf0TypedValue::unmarshall(&marshalled).unwrap();
500 assert_eq!(original, unmarshalled);
501 }
502
503 #[test]
504 fn test_object() {
505 let props = sample_properties();
506 let object_type = ObjectType::new(props);
507 let original = Amf0TypedValue::Object(object_type);
508 let marshalled = original.marshall().unwrap();
509 let (unmarshalled, _) = Amf0TypedValue::unmarshall(&marshalled).unwrap();
510 assert_eq!(original, unmarshalled);
511 }
512
513 #[test]
514 fn test_null() {
515 let original = Amf0TypedValue::Null(NullType);
516 let marshalled = original.marshall().unwrap();
517 let (unmarshalled, _) = Amf0TypedValue::unmarshall(&marshalled).unwrap();
518 assert_eq!(original, unmarshalled);
519 }
520
521 #[test]
522 fn test_undefined() {
523 let original = Amf0TypedValue::Undefined(UndefinedType);
524 let marshalled = original.marshall().unwrap();
525 let (unmarshalled, _) = Amf0TypedValue::unmarshall(&marshalled).unwrap();
526 assert_eq!(original, unmarshalled);
527 }
528
529 #[test]
530 fn test_ecma_array() {
531 let props = sample_properties();
532 let ecma_array_type = EcmaArrayType::new(props);
533 let original = Amf0TypedValue::EcmaArray(ecma_array_type);
534 let marshalled = original.marshall().unwrap();
535 let (unmarshalled, _) = Amf0TypedValue::unmarshall(&marshalled).unwrap();
536 assert_eq!(original, unmarshalled);
537 }
538
539 #[test]
540 fn test_object_end() {
541 let original = Amf0TypedValue::ObjectEnd(ObjectEndType::default());
542 let marshalled = original.marshall().unwrap();
543 let (unmarshalled, _) = Amf0TypedValue::unmarshall(&marshalled).unwrap();
544 assert_eq!(original, unmarshalled);
545 }
546
547 #[test]
548 fn test_long_string() {
549 let original =
550 Amf0TypedValue::LongString(LongStringType::new_from_string("a".repeat(65536)).unwrap());
551 let marshalled = original.marshall().unwrap();
552 let (unmarshalled, _) = Amf0TypedValue::unmarshall(&marshalled).unwrap();
553 assert_eq!(original, unmarshalled);
554 }
555
556 #[test]
558 fn test_amf0_typed_value_clone() {
559 let original = Amf0TypedValue::Object(ObjectType::new(sample_properties()));
560 let cloned = original.clone();
561 assert_eq!(original, cloned);
562 }
563
564 #[test]
565 fn test_amf0_typed_value_partial_eq() {
566 let num1 = Amf0TypedValue::Number(NumberType::new(42.0));
567 let num2 = Amf0TypedValue::Number(NumberType::new(42.0));
568 let num3 = Amf0TypedValue::Number(NumberType::new(43.0));
569 assert_eq!(num1, num2);
570 assert_ne!(num1, num3);
571
572 let obj = Amf0TypedValue::Object(ObjectType::new(sample_properties()));
573 let bool_val = Amf0TypedValue::Boolean(BooleanType::new(false));
574 assert_ne!(obj, bool_val);
575 }
576
577 #[test]
579 fn test_object_type() {
580 let props = sample_properties();
581 let original = ObjectType::new(props);
582 let marshalled = original.marshall().unwrap();
583 let (unmarshalled, _) = ObjectType::unmarshall(&marshalled).unwrap();
584 assert_eq!(original, unmarshalled);
585 }
586
587 #[test]
588 fn test_ecma_array_type() {
589 let props = sample_properties();
590 let original = EcmaArrayType::new(props);
591 let marshalled = original.marshall().unwrap();
592 let (unmarshalled, _) = EcmaArrayType::unmarshall(&marshalled).unwrap();
593 assert_eq!(original, unmarshalled);
594 }
595
596 #[test]
597 fn test_nested_type_clone() {
598 let original = ObjectType::new(sample_properties());
599 let cloned = original.clone();
600 assert_eq!(original, cloned);
601 }
602
603 #[test]
604 fn test_nested_type_partial_eq() {
605 let props1 = sample_properties();
606 let obj1 = ObjectType::new(props1.clone());
607 let obj2 = ObjectType::new(props1);
608 assert_eq!(obj1, obj2);
609
610 let mut props2 = IndexMap::new();
611 props2.insert(
612 Utf8::try_from("key1").unwrap(),
613 Amf0TypedValue::Number(NumberType::new(43.0)),
614 );
615 let obj3 = ObjectType::new(props2);
616 assert_ne!(obj1, obj3);
617 }
618
619 #[test]
621 fn test_unmarshall_invalid_type_marker() {
622 let buf = [0xff]; let result = Amf0TypedValue::unmarshall(&buf);
624 assert!(result.is_err());
625 }
626
627 #[test]
628 fn test_nested_type_buffer_too_small() {
629 let buf = [TypeMarker::Object as u8];
630 let result = ObjectType::unmarshall(&buf);
631 assert!(matches!(result, Err(AmfError::BufferTooSmall { .. })));
632 }
633}