1use crate::errors::serialize::DeError;
2use serde::ser::{Impossible, Serialize, Serializer};
3use serde::serde_if_integer128;
4use std::fmt::Write;
5
6/// A serializer, that ensures, that only plain types can be serialized,
7/// so result can be used as an XML tag or attribute name.
8///
9/// This serializer does not check that name does not contain characters that
10/// [not allowed] in XML names, because in some cases it should pass names
11/// that would be filtered on higher level.
12///
13/// [not allowed]: https://www.w3.org/TR/xml11/#sec-common-syn
14pub struct QNameSerializer<W: Write> {
15 /// Writer to which this serializer writes content
16 pub writer: W,
17}
18
19impl<W: Write> QNameSerializer<W> {
20 #[inline]
21 fn write_str(&mut self, value: &str) -> Result<(), DeError> {
22 Ok(self.writer.write_str(value)?)
23 }
24}
25
26impl<W: Write> Serializer for QNameSerializer<W> {
27 type Ok = W;
28 type Error = DeError;
29
30 type SerializeSeq = Impossible<Self::Ok, Self::Error>;
31 type SerializeTuple = Impossible<Self::Ok, Self::Error>;
32 type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
33 type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
34 type SerializeMap = Impossible<Self::Ok, Self::Error>;
35 type SerializeStruct = Impossible<Self::Ok, Self::Error>;
36 type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
37
38 write_primitive!();
39
40 fn serialize_str(mut self, value: &str) -> Result<Self::Ok, Self::Error> {
41 self.write_str(value)?;
42 Ok(self.writer)
43 }
44
45 /// Because unit type can be represented only by empty string which is not
46 /// a valid XML name, serialization of unit returns `Err(Unsupported)`
47 fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
48 Err(DeError::Unsupported(
49 "unit type `()` cannot be serialized as an XML tag name".into(),
50 ))
51 }
52
53 /// Because unit struct can be represented only by empty string which is not
54 /// a valid XML name, serialization of unit struct returns `Err(Unsupported)`
55 fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
56 Err(DeError::Unsupported(
57 format!(
58 "unit struct `{}` cannot be serialized as an XML tag name",
59 name
60 )
61 .into(),
62 ))
63 }
64
65 /// We cannot store both a variant discriminant and a variant value,
66 /// so serialization of enum newtype variant returns `Err(Unsupported)`
67 fn serialize_newtype_variant<T: ?Sized + Serialize>(
68 self,
69 name: &'static str,
70 _variant_index: u32,
71 variant: &'static str,
72 _value: &T,
73 ) -> Result<Self::Ok, DeError> {
74 Err(DeError::Unsupported(
75 format!(
76 "enum newtype variant `{}::{}` cannot be serialized as an XML tag name",
77 name, variant
78 )
79 .into(),
80 ))
81 }
82
83 fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
84 Err(DeError::Unsupported(
85 "sequence cannot be serialized as an XML tag name".into(),
86 ))
87 }
88
89 fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
90 Err(DeError::Unsupported(
91 "tuple cannot be serialized as an XML tag name".into(),
92 ))
93 }
94
95 fn serialize_tuple_struct(
96 self,
97 name: &'static str,
98 _len: usize,
99 ) -> Result<Self::SerializeTupleStruct, Self::Error> {
100 Err(DeError::Unsupported(
101 format!(
102 "tuple struct `{}` cannot be serialized as an XML tag name",
103 name
104 )
105 .into(),
106 ))
107 }
108
109 fn serialize_tuple_variant(
110 self,
111 name: &'static str,
112 _variant_index: u32,
113 variant: &'static str,
114 _len: usize,
115 ) -> Result<Self::SerializeTupleVariant, Self::Error> {
116 Err(DeError::Unsupported(
117 format!(
118 "enum tuple variant `{}::{}` cannot be serialized as an XML tag name",
119 name, variant
120 )
121 .into(),
122 ))
123 }
124
125 fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
126 Err(DeError::Unsupported(
127 "map cannot be serialized as an XML tag name".into(),
128 ))
129 }
130
131 fn serialize_struct(
132 self,
133 name: &'static str,
134 _len: usize,
135 ) -> Result<Self::SerializeStruct, Self::Error> {
136 Err(DeError::Unsupported(
137 format!("struct `{}` cannot be serialized as an XML tag name", name).into(),
138 ))
139 }
140
141 fn serialize_struct_variant(
142 self,
143 name: &'static str,
144 _variant_index: u32,
145 variant: &'static str,
146 _len: usize,
147 ) -> Result<Self::SerializeStructVariant, Self::Error> {
148 Err(DeError::Unsupported(
149 format!(
150 "enum struct variant `{}::{}` cannot be serialized as an XML tag name",
151 name, variant
152 )
153 .into(),
154 ))
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use crate::utils::Bytes;
162 use pretty_assertions::assert_eq;
163 use serde::Serialize;
164 use std::collections::BTreeMap;
165
166 #[derive(Debug, Serialize, PartialEq)]
167 struct Unit;
168
169 #[derive(Debug, Serialize, PartialEq)]
170 struct Newtype(bool);
171
172 #[derive(Debug, Serialize, PartialEq)]
173 struct Tuple(&'static str, usize);
174
175 #[derive(Debug, Serialize, PartialEq)]
176 struct Struct {
177 key: &'static str,
178 val: usize,
179 }
180
181 #[derive(Debug, Serialize, PartialEq)]
182 enum Enum {
183 Unit,
184 #[serde(rename = "<\"&'>")]
185 UnitEscaped,
186 Newtype(bool),
187 Tuple(&'static str, usize),
188 Struct {
189 key: &'static str,
190 val: usize,
191 },
192 }
193
194 /// Checks that given `$data` successfully serialized as `$expected`
195 macro_rules! serialize_as {
196 ($name:ident: $data:expr => $expected:literal) => {
197 #[test]
198 fn $name() {
199 let ser = QNameSerializer {
200 writer: String::new(),
201 };
202
203 let buffer = $data.serialize(ser).unwrap();
204 assert_eq!(buffer, $expected);
205 }
206 };
207 }
208
209 /// Checks that attempt to serialize given `$data` results to a
210 /// serialization error `$kind` with `$reason`
211 macro_rules! err {
212 ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
213 #[test]
214 fn $name() {
215 let mut buffer = String::new();
216 let ser = QNameSerializer {
217 writer: &mut buffer,
218 };
219
220 match $data.serialize(ser).unwrap_err() {
221 DeError::$kind(e) => assert_eq!(e, $reason),
222 e => panic!(
223 "Expected `{}({})`, found `{:?}`",
224 stringify!($kind),
225 $reason,
226 e
227 ),
228 }
229 assert_eq!(buffer, "");
230 }
231 };
232 }
233
234 serialize_as!(false_: false => "false");
235 serialize_as!(true_: true => "true");
236
237 serialize_as!(i8_: -42i8 => "-42");
238 serialize_as!(i16_: -4200i16 => "-4200");
239 serialize_as!(i32_: -42000000i32 => "-42000000");
240 serialize_as!(i64_: -42000000000000i64 => "-42000000000000");
241 serialize_as!(isize_: -42000000000000isize => "-42000000000000");
242
243 serialize_as!(u8_: 42u8 => "42");
244 serialize_as!(u16_: 4200u16 => "4200");
245 serialize_as!(u32_: 42000000u32 => "42000000");
246 serialize_as!(u64_: 42000000000000u64 => "42000000000000");
247 serialize_as!(usize_: 42000000000000usize => "42000000000000");
248
249 serde_if_integer128! {
250 serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
251 serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000");
252 }
253
254 serialize_as!(f32_: 4.2f32 => "4.2");
255 serialize_as!(f64_: 4.2f64 => "4.2");
256
257 serialize_as!(char_non_escaped: 'h' => "h");
258 serialize_as!(char_lt: '<' => "<");
259 serialize_as!(char_gt: '>' => ">");
260 serialize_as!(char_amp: '&' => "&");
261 serialize_as!(char_apos: '\'' => "'");
262 serialize_as!(char_quot: '"' => "\"");
263
264 serialize_as!(str_valid_name: "valid-name" => "valid-name");
265 serialize_as!(str_space: "string with spaces" => "string with spaces");
266 serialize_as!(str_lt: "string<" => "string<");
267 serialize_as!(str_gt: "string>" => "string>");
268 serialize_as!(str_amp: "string&" => "string&");
269 serialize_as!(str_apos: "string'" => "string'");
270 serialize_as!(str_quot: "string\"" => "string\"");
271
272 err!(bytes: Bytes(b"<\"escaped & bytes'>")
273 => Unsupported("`serialize_bytes` not supported yet"));
274
275 serialize_as!(option_none: Option::<&str>::None => "");
276 serialize_as!(option_some: Some("non-escaped-string") => "non-escaped-string");
277
278 err!(unit: ()
279 => Unsupported("unit type `()` cannot be serialized as an XML tag name"));
280 err!(unit_struct: Unit
281 => Unsupported("unit struct `Unit` cannot be serialized as an XML tag name"));
282
283 serialize_as!(enum_unit: Enum::Unit => "Unit");
284 serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "<\"&'>");
285
286 serialize_as!(newtype: Newtype(true) => "true");
287 err!(enum_newtype: Enum::Newtype(false)
288 => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an XML tag name"));
289
290 err!(seq: vec![1, 2, 3]
291 => Unsupported("sequence cannot be serialized as an XML tag name"));
292 err!(tuple: ("<\"&'>", "with\t\r\n spaces", 3usize)
293 => Unsupported("tuple cannot be serialized as an XML tag name"));
294 err!(tuple_struct: Tuple("first", 42)
295 => Unsupported("tuple struct `Tuple` cannot be serialized as an XML tag name"));
296 err!(enum_tuple: Enum::Tuple("first", 42)
297 => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an XML tag name"));
298
299 err!(map: BTreeMap::from([("_1", 2), ("_3", 4)])
300 => Unsupported("map cannot be serialized as an XML tag name"));
301 err!(struct_: Struct { key: "answer", val: 42 }
302 => Unsupported("struct `Struct` cannot be serialized as an XML tag name"));
303 err!(enum_struct: Enum::Struct { key: "answer", val: 42 }
304 => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an XML tag name"));
305}
306