1use crate::types::{Element, Value};
3use crate::PADDING;
4use cookie_factory::bytes::{be_f64, be_u16, be_u32, be_u8};
5use cookie_factory::{SerializeFn, WriteContext};
6use std::io::Write;
7
8use crate::amf0::type_marker::TypeMarker;
9use crate::amf3::write::AMF3Encoder;
10use crate::nom_utils::write_string;
11use cookie_factory::combinator::slice;
12use cookie_factory::combinator::string;
13use cookie_factory::multi::all;
14use cookie_factory::sequence::tuple;
15use std::ops::Deref;
16use std::rc::Rc;
17
18fn write_type_marker<'a, 'b: 'a, W: Write + 'a>(type_: TypeMarker) -> impl SerializeFn<W> + 'a {
19 be_u8(type_ as u8)
20}
21
22fn write_number_element<'a, 'b: 'a, W: Write + 'a>(s: f64) -> impl SerializeFn<W> + 'a {
23 tuple((write_type_marker(TypeMarker::Number), be_f64(s)))
24}
25
26fn write_bool_element<'a, 'b: 'a, W: Write + 'a>(s: bool) -> impl SerializeFn<W> + 'a {
27 tuple((
28 write_type_marker(TypeMarker::Boolean),
29 be_u8(if s { 1u8 } else { 0u8 }),
30 ))
31}
32
33fn write_long_string_content<'a, 'b: 'a, W: Write + 'a>(s: &'b str) -> impl SerializeFn<W> + 'a {
34 tuple((be_u32(s.len() as u32), string(s)))
35}
36
37fn write_long_string_element<'a, 'b: 'a, W: Write + 'a>(s: &'b str) -> impl SerializeFn<W> + 'a {
38 tuple((
39 write_type_marker(TypeMarker::LongString),
40 write_long_string_content(s),
41 ))
42}
43
44fn write_string_element<'a, 'b: 'a, W: Write + 'a>(s: &'b str) -> impl SerializeFn<W> + 'a {
45 tuple((write_type_marker(TypeMarker::String), write_string(s)))
46}
47
48fn write_object_element<'a, 'b: 'a, W: Write + 'a>(o: &'b [Element]) -> impl SerializeFn<W> + 'a {
49 tuple((
50 write_type_marker(TypeMarker::Object),
51 all(o.iter().map(write_element)),
52 be_u16(0),
53 write_type_marker(TypeMarker::ObjectEnd),
54 ))
55}
56
57fn write_null_element<'a, 'b: 'a, W: Write + 'a>() -> impl SerializeFn<W> + 'a {
58 write_type_marker(TypeMarker::Null)
59}
60
61fn write_undefined_element<'a, 'b: 'a, W: Write + 'a>() -> impl SerializeFn<W> + 'a {
62 write_type_marker(TypeMarker::Undefined)
63}
64
65fn write_strict_array_element<'a, 'b: 'a, W: Write + 'a>(
66 elements: &'b [Rc<Value>],
67) -> impl SerializeFn<W> + 'a {
68 tuple((
69 write_type_marker(TypeMarker::Array),
70 be_u32(elements.len() as u32),
71 all(elements.iter().map(write_value)),
72 ))
73}
74
75fn write_date_element<'a, 'b: 'a, W: Write + 'a>(
76 date: f64,
77 tz: Option<u16>,
78) -> impl SerializeFn<W> + 'a {
79 tuple((
80 write_type_marker(TypeMarker::Date),
81 be_f64(date),
82 be_u16(tz.unwrap_or(0)),
83 ))
84}
85
86fn write_unsupported_element<'a, 'b: 'a, W: Write + 'a>() -> impl SerializeFn<W> + 'a {
87 write_type_marker(TypeMarker::Unsupported)
88}
89
90fn write_xml_element<'a, 'b: 'a, W: Write + 'a>(content: &'b str) -> impl SerializeFn<W> + 'a {
91 tuple((
92 write_type_marker(TypeMarker::XML),
93 write_long_string_content(content),
94 ))
95}
96
97fn write_typed_object_element<'a, 'b: 'a, W: Write + 'a>(
98 name: &'b str,
99 elements: &'b [Element],
100) -> impl SerializeFn<W> + 'a {
101 tuple((
102 write_type_marker(TypeMarker::TypedObject),
103 write_string(name),
104 all(elements.iter().map(write_element)),
105 be_u16(0),
106 write_type_marker(TypeMarker::ObjectEnd),
107 ))
108}
109
110fn write_mixed_array<'a, 'b: 'a, W: Write + 'a>(
111 elements: &'b [Element],
112 length: u32,
113) -> impl SerializeFn<W> + 'a {
114 tuple((
118 write_type_marker(TypeMarker::MixedArrayStart),
119 be_u32(length),
120 all(elements.iter().map(write_element)),
121 be_u16(0),
122 write_type_marker(TypeMarker::ObjectEnd),
123 ))
124}
125
126fn write_value<'a, 'b: 'a, W: Write + 'a>(element: &'b Rc<Value>) -> impl SerializeFn<W> + 'a {
127 move |out: WriteContext<W>| match element.deref() {
128 Value::Number(n) => write_number_element(*n)(out),
129 Value::Bool(b) => write_bool_element(*b)(out),
130 Value::String(s) => {
131 if s.len() > 65535 {
132 write_long_string_element(s)(out)
133 } else {
134 write_string_element(s)(out)
135 }
136 }
137 Value::Object(elements, class_def) => {
138 if let Some(class_def) = class_def {
139 write_typed_object_element(&class_def.name, elements)(out)
140 } else {
141 write_object_element(elements)(out)
142 }
143 }
144 Value::Null => write_null_element()(out),
145 Value::Undefined => write_undefined_element()(out),
146 Value::StrictArray(a) => write_strict_array_element(a.as_slice())(out),
147 Value::Date(d, tz) => write_date_element(*d, *tz)(out),
148 Value::Unsupported => write_unsupported_element()(out),
149 Value::XML(x, _string) => write_xml_element(x)(out),
150 Value::ECMAArray(_dense, elems, elems_length) => {
151 write_mixed_array(elems, *elems_length)(out)
152 }
153 Value::AMF3(e) => AMF3Encoder::default().write_value_element(e)(out),
154 _ => {
155 write_unsupported_element()(out) }
157 }
158}
159
160fn write_element<'a, 'b: 'a, W: Write + 'a>(element: &'b Element) -> impl SerializeFn<W> + 'a {
161 tuple((write_string(&element.name), write_value(&element.value)))
162}
163
164fn write_element_and_padding<'a, 'b: 'a, W: Write + 'a>(
165 element: &'b Element,
166) -> impl SerializeFn<W> + 'a {
167 tuple((write_element(element), slice(PADDING)))
168}
169
170pub(crate) fn write_body<'a, 'b: 'a, W: Write + 'a>(
171 elements: &'b [Element],
172) -> impl SerializeFn<W> + 'a {
173 all(elements.iter().map(write_element_and_padding))
174}