1use std::borrow::Cow;
2use std::io;
3
4use byteorder::{BigEndian, WriteBytesExt};
5
6use super::define::Amf0Marker;
7use super::{Amf0Value, Amf0WriteError};
8
9pub struct Amf0Encoder;
13
14impl Amf0Encoder {
15 pub fn encode(writer: &mut impl io::Write, value: &Amf0Value) -> Result<(), Amf0WriteError> {
17 match value {
18 Amf0Value::Boolean(val) => Self::encode_bool(writer, *val),
19 Amf0Value::Null => Self::encode_null(writer),
20 Amf0Value::Number(val) => Self::encode_number(writer, *val),
21 Amf0Value::String(val) => Self::encode_string(writer, val),
22 Amf0Value::Object(val) => Self::encode_object(writer, val),
23 _ => Err(Amf0WriteError::UnsupportedType(value.marker())),
24 }
25 }
26
27 fn object_eof(writer: &mut impl io::Write) -> Result<(), Amf0WriteError> {
28 writer.write_u24::<BigEndian>(Amf0Marker::ObjectEnd as u32)?;
29 Ok(())
30 }
31
32 pub fn encode_number(writer: &mut impl io::Write, value: f64) -> Result<(), Amf0WriteError> {
34 writer.write_u8(Amf0Marker::Number as u8)?;
35 writer.write_f64::<BigEndian>(value)?;
36 Ok(())
37 }
38
39 pub fn encode_bool(writer: &mut impl io::Write, value: bool) -> Result<(), Amf0WriteError> {
41 writer.write_u8(Amf0Marker::Boolean as u8)?;
42 writer.write_u8(value as u8)?;
43 Ok(())
44 }
45
46 pub fn encode_string(writer: &mut impl io::Write, value: &str) -> Result<(), Amf0WriteError> {
48 if value.len() > (u16::MAX as usize) {
49 return Err(Amf0WriteError::NormalStringTooLong);
50 }
51
52 writer.write_u8(Amf0Marker::String as u8)?;
53 writer.write_u16::<BigEndian>(value.len() as u16)?;
54 writer.write_all(value.as_bytes())?;
55 Ok(())
56 }
57
58 pub fn encode_null(writer: &mut impl io::Write) -> Result<(), Amf0WriteError> {
60 writer.write_u8(Amf0Marker::Null as u8)?;
61 Ok(())
62 }
63
64 pub fn encode_object(
66 writer: &mut impl io::Write,
67 properties: &[(Cow<'_, str>, Amf0Value<'_>)],
68 ) -> Result<(), Amf0WriteError> {
69 writer.write_u8(Amf0Marker::Object as u8)?;
70 for (key, value) in properties {
71 writer.write_u16::<BigEndian>(key.len() as u16)?;
72 writer.write_all(key.as_bytes())?;
73 Self::encode(writer, value)?;
74 }
75
76 Self::object_eof(writer)?;
77 Ok(())
78 }
79}
80
81#[cfg(test)]
82#[cfg_attr(all(test, coverage_nightly), coverage(off))]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn test_write_number() {
88 let mut amf0_number = vec![0x00];
89 amf0_number.extend_from_slice(&772.161_f64.to_be_bytes());
90
91 let mut vec = Vec::<u8>::new();
92
93 Amf0Encoder::encode_number(&mut vec, 772.161).unwrap();
94
95 assert_eq!(vec, amf0_number);
96 }
97
98 #[test]
99 fn test_write_boolean() {
100 let amf0_boolean = vec![0x01, 0x01];
101
102 let mut vec = Vec::<u8>::new();
103
104 Amf0Encoder::encode_bool(&mut vec, true).unwrap();
105
106 assert_eq!(vec, amf0_boolean);
107 }
108
109 #[test]
110 fn test_write_string() {
111 let mut amf0_string = vec![0x02, 0x00, 0x0b];
112 amf0_string.extend_from_slice(b"Hello World");
113
114 let mut vec = Vec::<u8>::new();
115
116 Amf0Encoder::encode_string(&mut vec, "Hello World").unwrap();
117
118 assert_eq!(vec, amf0_string);
119 }
120
121 #[test]
122 fn test_write_null() {
123 let amf0_null = vec![0x05];
124
125 let mut vec = Vec::<u8>::new();
126
127 Amf0Encoder::encode_null(&mut vec).unwrap();
128
129 assert_eq!(vec, amf0_null);
130 }
131
132 #[test]
133 fn test_write_object() {
134 let mut amf0_object = vec![0x03, 0x00, 0x04];
135 amf0_object.extend_from_slice(b"test");
136 amf0_object.extend_from_slice(&[0x05]);
137 amf0_object.extend_from_slice(&[0x00, 0x00, 0x09]);
138
139 let mut vec = Vec::<u8>::new();
140
141 Amf0Encoder::encode_object(&mut vec, &[("test".into(), Amf0Value::Null)]).unwrap();
142
143 assert_eq!(vec, amf0_object);
144 }
145
146 #[test]
147 fn test_encode_boolean() {
148 let amf0_boolean_true = vec![Amf0Marker::Boolean as u8, 0x01];
149 let amf0_boolean_false = vec![Amf0Marker::Boolean as u8, 0x00];
150
151 let mut vec_true = Vec::<u8>::new();
152 let mut vec_false = Vec::<u8>::new();
153
154 Amf0Encoder::encode(&mut vec_true, &Amf0Value::Boolean(true)).unwrap();
155 assert_eq!(vec_true, amf0_boolean_true);
156 Amf0Encoder::encode(&mut vec_false, &Amf0Value::Boolean(false)).unwrap();
157 assert_eq!(vec_false, amf0_boolean_false);
158 }
159
160 #[test]
161 fn test_encode_object() {
162 let mut amf0_object = vec![Amf0Marker::Object as u8, 0x00, 0x04];
163 amf0_object.extend_from_slice(b"test");
164 amf0_object.push(Amf0Marker::Null as u8);
165 amf0_object.extend_from_slice(&[0x00, 0x00, 0x09]);
166 let mut vec = Vec::<u8>::new();
167
168 Amf0Encoder::encode(&mut vec, &Amf0Value::Object(vec![("test".into(), Amf0Value::Null)].into())).unwrap();
169 assert_eq!(vec, amf0_object);
170 }
171
172 #[test]
173 fn test_encode_generic_error_unsupported_type() {
174 let mut writer = Vec::<u8>::new();
175 let unsupported_marker = Amf0Value::ObjectEnd;
176 let result = Amf0Encoder::encode(&mut writer, &unsupported_marker);
177 assert!(matches!(result, Err(Amf0WriteError::UnsupportedType(_))));
178 }
179
180 #[test]
181 fn test_encode_string_too_long() {
182 let long_string = "a".repeat(u16::MAX as usize + 1);
183 let mut writer = Vec::<u8>::new();
184 let result = Amf0Encoder::encode_string(&mut writer, &long_string);
185 assert!(matches!(result, Err(Amf0WriteError::NormalStringTooLong)));
186 }
187}