/rust/registry/src/index.crates.io-1949cf8c6b5b557f/quick-xml-0.38.4/src/se/content.rs
Line | Count | Source |
1 | | //! Contains serializer for content of an XML element |
2 | | |
3 | | use crate::de::TEXT_KEY; |
4 | | use crate::se::element::{ElementSerializer, Struct, Tuple}; |
5 | | use crate::se::simple_type::{QuoteTarget, SimpleTypeSerializer}; |
6 | | use crate::se::{Indent, QuoteLevel, SeError, TextFormat, WriteResult, XmlName}; |
7 | | use serde::ser::{ |
8 | | Impossible, Serialize, SerializeSeq, SerializeTuple, SerializeTupleStruct, Serializer, |
9 | | }; |
10 | | use serde::serde_if_integer128; |
11 | | use std::fmt::Write; |
12 | | |
13 | | macro_rules! write_primitive { |
14 | | ($method:ident ( $ty:ty )) => { |
15 | | #[inline] |
16 | 0 | fn $method(self, value: $ty) -> Result<Self::Ok, Self::Error> { |
17 | 0 | self.into_simple_type_serializer()?.$method(value)?; |
18 | 0 | Ok(WriteResult::Text) |
19 | 0 | } Unexecuted instantiation: <quick_xml::se::content::ContentSerializer<_> as serde_core::ser::Serializer>::serialize_i8 Unexecuted instantiation: <quick_xml::se::content::ContentSerializer<_> as serde_core::ser::Serializer>::serialize_u8 Unexecuted instantiation: <quick_xml::se::content::ContentSerializer<_> as serde_core::ser::Serializer>::serialize_f32 Unexecuted instantiation: <quick_xml::se::content::ContentSerializer<_> as serde_core::ser::Serializer>::serialize_f64 Unexecuted instantiation: <quick_xml::se::content::ContentSerializer<_> as serde_core::ser::Serializer>::serialize_i16 Unexecuted instantiation: <quick_xml::se::content::ContentSerializer<_> as serde_core::ser::Serializer>::serialize_i32 Unexecuted instantiation: <quick_xml::se::content::ContentSerializer<_> as serde_core::ser::Serializer>::serialize_i64 Unexecuted instantiation: <quick_xml::se::content::ContentSerializer<_> as serde_core::ser::Serializer>::serialize_u16 Unexecuted instantiation: <quick_xml::se::content::ContentSerializer<_> as serde_core::ser::Serializer>::serialize_u32 Unexecuted instantiation: <quick_xml::se::content::ContentSerializer<_> as serde_core::ser::Serializer>::serialize_u64 Unexecuted instantiation: <quick_xml::se::content::ContentSerializer<_> as serde_core::ser::Serializer>::serialize_bool Unexecuted instantiation: <quick_xml::se::content::ContentSerializer<_> as serde_core::ser::Serializer>::serialize_i128 Unexecuted instantiation: <quick_xml::se::content::ContentSerializer<_> as serde_core::ser::Serializer>::serialize_u128 Unexecuted instantiation: <quick_xml::se::content::ContentSerializer<_> as serde_core::ser::Serializer>::serialize_bytes |
20 | | }; |
21 | | } |
22 | | |
23 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
24 | | |
25 | | /// A serializer used to serialize content of an element. It does not write |
26 | | /// surrounding tags. Unlike the [`ElementSerializer`], this serializer serializes |
27 | | /// enums using variant names as tag names, i. e. as `<variant>...</variant>`. |
28 | | /// |
29 | | /// Returns the classification of the last written type. |
30 | | /// |
31 | | /// This serializer does the following: |
32 | | /// - numbers converted to a decimal representation and serialized as naked strings; |
33 | | /// - booleans serialized ether as `"true"` or `"false"`; |
34 | | /// - strings and characters are serialized as naked strings; |
35 | | /// - `None` does not write anything; |
36 | | /// - `Some` and newtypes are serialized as an inner type using the same serializer; |
37 | | /// - units (`()`) and unit structs does not write anything; |
38 | | /// - sequences, tuples and tuple structs are serialized without delimiters. |
39 | | /// `[1, 2, 3]` would be serialized as `123` (if not using indent); |
40 | | /// - structs and maps are not supported ([`SeError::Unsupported`] is returned); |
41 | | /// - enums: |
42 | | /// - unit variants are serialized as self-closed `<variant/>`; |
43 | | /// - newtype variants are serialized as inner value wrapped in `<variant>...</variant>`; |
44 | | /// - tuple variants are serialized as sequences where each element is wrapped |
45 | | /// in `<variant>...</variant>`; |
46 | | /// - struct variants are serialized as a sequence of fields wrapped in |
47 | | /// `<variant>...</variant>`. Each field is serialized recursively using |
48 | | /// either [`ElementSerializer`], `ContentSerializer` (`$value` fields), or |
49 | | /// [`SimpleTypeSerializer`] (`$text` fields). In particular, the empty struct |
50 | | /// is serialized as `<variant/>`; |
51 | | /// |
52 | | /// Usage of empty tags depends on the [`Self::expand_empty_elements`] setting. |
53 | | /// |
54 | | /// The difference between this serializer and [`SimpleTypeSerializer`] is in how |
55 | | /// sequences and maps are serialized. Unlike `SimpleTypeSerializer` it supports |
56 | | /// any types in sequences and serializes them as list of elements, but that has |
57 | | /// drawbacks. Sequence of primitives would be serialized without delimiters and |
58 | | /// it will be impossible to distinguish between them. Even worse, when serializing |
59 | | /// with indent, sequence of strings become one big string with additional content |
60 | | /// and it would be impossible to distinguish between content of the original |
61 | | /// strings and inserted indent characters. |
62 | | pub struct ContentSerializer<'w, 'i, W: Write> { |
63 | | pub writer: &'w mut W, |
64 | | /// Defines which XML characters need to be escaped in text content |
65 | | pub level: QuoteLevel, |
66 | | /// Current indentation level. Note, that `Indent::None` means that there is |
67 | | /// no indentation at all, but `write_indent == false` means only, that indent |
68 | | /// writing is disabled in this instantiation of `ContentSerializer`, but |
69 | | /// child serializers should have access to the actual state of indentation. |
70 | | pub(super) indent: Indent<'i>, |
71 | | /// If `true`, then current indent will be written before writing the content, |
72 | | /// but only if content is not empty. This flag is reset after writing indent. |
73 | | pub write_indent: bool, |
74 | | /// Defines how text content should be serialized (as escaped text or CDATA) |
75 | | pub text_format: TextFormat, |
76 | | /// If `true`, then primitive types that serializes to a text content without |
77 | | /// surrounding tag will be allowed, otherwise the [`SeError::Unsupported`] |
78 | | /// will be returned. |
79 | | /// |
80 | | /// This method protects from the situation when two consequent values serialized |
81 | | /// as a text that makes it impossible to distinguish between them during |
82 | | /// deserialization. Instead of ambiguous serialization the error is returned. |
83 | | pub allow_primitive: bool, |
84 | | // If `true`, then empty elements will be serialized as `<element></element>` |
85 | | // instead of `<element/>`. |
86 | | pub expand_empty_elements: bool, |
87 | | } |
88 | | |
89 | | impl<'w, 'i, W: Write> ContentSerializer<'w, 'i, W> { |
90 | | /// Turns this serializer into serializer of a text content |
91 | | #[inline] |
92 | 0 | pub fn into_simple_type_serializer_impl(self) -> SimpleTypeSerializer<&'w mut W> { |
93 | | SimpleTypeSerializer { |
94 | 0 | writer: self.writer, |
95 | 0 | target: match self.text_format { |
96 | 0 | TextFormat::Text => QuoteTarget::Text, |
97 | 0 | TextFormat::CData => QuoteTarget::CData, |
98 | | }, |
99 | 0 | level: self.level, |
100 | | } |
101 | 0 | } |
102 | | |
103 | | /// Turns this serializer into serializer of a text content if that is allowed, |
104 | | /// otherwise error is returned |
105 | | #[inline] |
106 | 0 | pub fn into_simple_type_serializer(self) -> Result<SimpleTypeSerializer<&'w mut W>, SeError> { |
107 | 0 | if self.allow_primitive { |
108 | 0 | Ok(self.into_simple_type_serializer_impl()) |
109 | | } else { |
110 | 0 | Err(SeError::Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back".into())) |
111 | | } |
112 | 0 | } |
113 | | |
114 | | /// Creates new serializer that shares state with this serializer and |
115 | | /// writes to the same underlying writer |
116 | | #[inline] |
117 | 0 | pub fn new_seq_element_serializer( |
118 | 0 | &mut self, |
119 | 0 | allow_primitive: bool, |
120 | 0 | ) -> ContentSerializer<'_, '_, W> { |
121 | 0 | ContentSerializer { |
122 | 0 | writer: self.writer, |
123 | 0 | level: self.level, |
124 | 0 | indent: self.indent.borrow(), |
125 | 0 | write_indent: self.write_indent, |
126 | 0 | text_format: self.text_format, |
127 | 0 | allow_primitive, |
128 | 0 | expand_empty_elements: self.expand_empty_elements, |
129 | 0 | } |
130 | 0 | } |
131 | | |
132 | | /// Writes `name` as self-closed tag |
133 | | #[inline] |
134 | 0 | pub(super) fn write_empty(mut self, name: XmlName) -> Result<WriteResult, SeError> { |
135 | 0 | self.write_indent()?; |
136 | 0 | if self.expand_empty_elements { |
137 | 0 | self.writer.write_char('<')?; |
138 | 0 | self.writer.write_str(name.0)?; |
139 | 0 | self.writer.write_str("></")?; |
140 | 0 | self.writer.write_str(name.0)?; |
141 | 0 | self.writer.write_char('>')?; |
142 | | } else { |
143 | 0 | self.writer.write_str("<")?; |
144 | 0 | self.writer.write_str(name.0)?; |
145 | 0 | self.writer.write_str("/>")?; |
146 | | } |
147 | 0 | Ok(WriteResult::Element) |
148 | 0 | } |
149 | | |
150 | | /// Writes simple type content between `name` tags |
151 | 0 | pub(super) fn write_wrapped<S>( |
152 | 0 | mut self, |
153 | 0 | name: XmlName, |
154 | 0 | serialize: S, |
155 | 0 | ) -> Result<WriteResult, SeError> |
156 | 0 | where |
157 | 0 | S: for<'a> FnOnce(SimpleTypeSerializer<&'a mut W>) -> Result<&'a mut W, SeError>, |
158 | | { |
159 | 0 | self.write_indent()?; |
160 | 0 | self.writer.write_char('<')?; |
161 | 0 | self.writer.write_str(name.0)?; |
162 | 0 | self.writer.write_char('>')?; |
163 | | |
164 | 0 | let writer = serialize(self.into_simple_type_serializer_impl())?; |
165 | | |
166 | 0 | writer.write_str("</")?; |
167 | 0 | writer.write_str(name.0)?; |
168 | 0 | writer.write_char('>')?; |
169 | 0 | Ok(WriteResult::Element) |
170 | 0 | } |
171 | | |
172 | 0 | pub(super) fn write_indent(&mut self) -> Result<(), SeError> { |
173 | 0 | if self.write_indent { |
174 | 0 | self.indent.write_indent(&mut self.writer)?; |
175 | 0 | self.write_indent = false; |
176 | 0 | } |
177 | 0 | Ok(()) |
178 | 0 | } |
179 | | } |
180 | | |
181 | | impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> { |
182 | | type Ok = WriteResult; |
183 | | type Error = SeError; |
184 | | |
185 | | type SerializeSeq = Seq<'w, 'i, W>; |
186 | | type SerializeTuple = Seq<'w, 'i, W>; |
187 | | type SerializeTupleStruct = Seq<'w, 'i, W>; |
188 | | type SerializeTupleVariant = Tuple<'w, 'i, W>; |
189 | | type SerializeMap = Impossible<Self::Ok, Self::Error>; |
190 | | type SerializeStruct = Struct<'w, 'i, W>; |
191 | | type SerializeStructVariant = Struct<'w, 'i, W>; |
192 | | |
193 | | write_primitive!(serialize_bool(bool)); |
194 | | |
195 | | write_primitive!(serialize_i8(i8)); |
196 | | write_primitive!(serialize_i16(i16)); |
197 | | write_primitive!(serialize_i32(i32)); |
198 | | write_primitive!(serialize_i64(i64)); |
199 | | |
200 | | write_primitive!(serialize_u8(u8)); |
201 | | write_primitive!(serialize_u16(u16)); |
202 | | write_primitive!(serialize_u32(u32)); |
203 | | write_primitive!(serialize_u64(u64)); |
204 | | |
205 | | serde_if_integer128! { |
206 | | write_primitive!(serialize_i128(i128)); |
207 | | write_primitive!(serialize_u128(u128)); |
208 | | } |
209 | | |
210 | | write_primitive!(serialize_f32(f32)); |
211 | | write_primitive!(serialize_f64(f64)); |
212 | | |
213 | | write_primitive!(serialize_bytes(&[u8])); |
214 | | |
215 | | #[inline] |
216 | 0 | fn serialize_char(self, value: char) -> Result<Self::Ok, Self::Error> { |
217 | 0 | self.into_simple_type_serializer()?.serialize_char(value)?; |
218 | 0 | Ok(WriteResult::SensitiveText) |
219 | 0 | } |
220 | | |
221 | | #[inline] |
222 | 0 | fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> { |
223 | 0 | if !value.is_empty() { |
224 | 0 | self.into_simple_type_serializer()?.serialize_str(value)?; |
225 | 0 | } |
226 | 0 | Ok(WriteResult::SensitiveText) |
227 | 0 | } |
228 | | |
229 | | /// Does not write anything |
230 | | #[inline] |
231 | 0 | fn serialize_none(self) -> Result<Self::Ok, Self::Error> { |
232 | | // Classify `None` as sensitive to whitespaces, because this can be `Option<String>`. |
233 | | // Unfortunately, we do not known what the type the option contains, so have no chance |
234 | | // to adapt our behavior to it. The safe variant is assume sensitiviness |
235 | 0 | Ok(WriteResult::SensitiveNothing) |
236 | 0 | } |
237 | | |
238 | 0 | fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, Self::Error> { |
239 | 0 | value.serialize(self) |
240 | 0 | } |
241 | | |
242 | | /// Does not write anything |
243 | | #[inline] |
244 | 0 | fn serialize_unit(self) -> Result<Self::Ok, Self::Error> { |
245 | 0 | Ok(WriteResult::Nothing) |
246 | 0 | } |
247 | | |
248 | | /// Does not write anything |
249 | | #[inline] |
250 | 0 | fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> { |
251 | 0 | Ok(WriteResult::Nothing) |
252 | 0 | } |
253 | | |
254 | | /// If `variant` is a special `$text` variant, then do nothing, otherwise |
255 | | /// checks `variant` for XML name validity and writes `<variant/>`. |
256 | 0 | fn serialize_unit_variant( |
257 | 0 | self, |
258 | 0 | _name: &'static str, |
259 | 0 | _variant_index: u32, |
260 | 0 | variant: &'static str, |
261 | 0 | ) -> Result<Self::Ok, Self::Error> { |
262 | 0 | if variant == TEXT_KEY { |
263 | 0 | Ok(WriteResult::Nothing) |
264 | | } else { |
265 | 0 | let name = XmlName::try_from(variant)?; |
266 | 0 | self.write_empty(name) |
267 | | } |
268 | 0 | } |
269 | | |
270 | 0 | fn serialize_newtype_struct<T: ?Sized + Serialize>( |
271 | 0 | self, |
272 | 0 | _name: &'static str, |
273 | 0 | value: &T, |
274 | 0 | ) -> Result<Self::Ok, Self::Error> { |
275 | 0 | value.serialize(self) |
276 | 0 | } |
277 | | |
278 | | /// If `variant` is a special `$text` variant, then writes `value` as a `xs:simpleType`, |
279 | | /// otherwise checks `variant` for XML name validity and writes `value` as a new |
280 | | /// `<variant>` element. |
281 | 0 | fn serialize_newtype_variant<T: ?Sized + Serialize>( |
282 | 0 | self, |
283 | 0 | _name: &'static str, |
284 | 0 | _variant_index: u32, |
285 | 0 | variant: &'static str, |
286 | 0 | value: &T, |
287 | 0 | ) -> Result<Self::Ok, Self::Error> { |
288 | 0 | if variant == TEXT_KEY { |
289 | 0 | value.serialize(self.into_simple_type_serializer()?)?; |
290 | 0 | Ok(WriteResult::SensitiveText) |
291 | | } else { |
292 | 0 | value.serialize(ElementSerializer { |
293 | 0 | key: XmlName::try_from(variant)?, |
294 | 0 | ser: self, |
295 | 0 | })?; |
296 | 0 | Ok(WriteResult::Element) |
297 | | } |
298 | 0 | } |
299 | | |
300 | | #[inline] |
301 | 0 | fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> { |
302 | 0 | Ok(Seq { |
303 | 0 | ser: self, |
304 | 0 | // If sequence if empty, nothing will be serialized. Because sequence can be of `Option`s |
305 | 0 | // we need to assume that writing indent may change the data and do not write anything |
306 | 0 | last: WriteResult::SensitiveNothing, |
307 | 0 | }) |
308 | 0 | } |
309 | | |
310 | | #[inline] |
311 | 0 | fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> { |
312 | 0 | self.serialize_seq(Some(len)) |
313 | 0 | } |
314 | | |
315 | | #[inline] |
316 | 0 | fn serialize_tuple_struct( |
317 | 0 | self, |
318 | 0 | _name: &'static str, |
319 | 0 | len: usize, |
320 | 0 | ) -> Result<Self::SerializeTupleStruct, Self::Error> { |
321 | 0 | self.serialize_tuple(len) |
322 | 0 | } |
323 | | |
324 | | /// Serializes variant as a tuple with name `variant`, producing |
325 | | /// |
326 | | /// ```xml |
327 | | /// <variant><!-- 1st element of a tuple --></variant> |
328 | | /// <variant><!-- 2nd element of a tuple --></variant> |
329 | | /// <!-- ... --> |
330 | | /// <variant><!-- Nth element of a tuple --></variant> |
331 | | /// ``` |
332 | | #[inline] |
333 | 0 | fn serialize_tuple_variant( |
334 | 0 | self, |
335 | 0 | name: &'static str, |
336 | 0 | _variant_index: u32, |
337 | 0 | variant: &'static str, |
338 | 0 | len: usize, |
339 | 0 | ) -> Result<Self::SerializeTupleVariant, Self::Error> { |
340 | 0 | if variant == TEXT_KEY { |
341 | 0 | self.into_simple_type_serializer()? |
342 | 0 | .serialize_tuple_struct(name, len) |
343 | 0 | .map(Tuple::Text) |
344 | | } else { |
345 | 0 | let ser = ElementSerializer { |
346 | 0 | key: XmlName::try_from(variant)?, |
347 | 0 | ser: self, |
348 | | }; |
349 | 0 | ser.serialize_tuple_struct(name, len).map(Tuple::Element) |
350 | | } |
351 | 0 | } |
352 | | |
353 | 0 | fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> { |
354 | 0 | Err(SeError::Unsupported( |
355 | 0 | "serialization of map types is not supported in `$value` field".into(), |
356 | 0 | )) |
357 | 0 | } |
358 | | |
359 | | #[inline] |
360 | 0 | fn serialize_struct( |
361 | 0 | self, |
362 | 0 | name: &'static str, |
363 | 0 | len: usize, |
364 | 0 | ) -> Result<Self::SerializeStruct, Self::Error> { |
365 | | ElementSerializer { |
366 | 0 | ser: self, |
367 | 0 | key: XmlName::try_from(name)?, |
368 | | } |
369 | 0 | .serialize_struct(name, len) |
370 | 0 | } |
371 | | |
372 | | /// Serializes variant as an element with name `variant`, producing |
373 | | /// |
374 | | /// ```xml |
375 | | /// <variant> |
376 | | /// <!-- struct fields... --> |
377 | | /// </variant> |
378 | | /// ``` |
379 | | /// |
380 | | /// If struct has no fields which is represented by nested elements or a text, |
381 | | /// it may be serialized as self-closed element `<variant/>`. |
382 | | #[inline] |
383 | 0 | fn serialize_struct_variant( |
384 | 0 | self, |
385 | 0 | name: &'static str, |
386 | 0 | _variant_index: u32, |
387 | 0 | variant: &'static str, |
388 | 0 | len: usize, |
389 | 0 | ) -> Result<Self::SerializeStructVariant, Self::Error> { |
390 | 0 | if variant == TEXT_KEY { |
391 | 0 | Err(SeError::Unsupported( |
392 | 0 | format!("cannot serialize `$text` struct variant of `{}` enum", name).into(), |
393 | 0 | )) |
394 | | } else { |
395 | 0 | let ser = ElementSerializer { |
396 | 0 | key: XmlName::try_from(variant)?, |
397 | 0 | ser: self, |
398 | | }; |
399 | 0 | ser.serialize_struct(name, len) |
400 | | } |
401 | 0 | } |
402 | | } |
403 | | |
404 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
405 | | |
406 | | /// Helper struct which remembers the classification of the last serialized element |
407 | | /// and reports it when the sequence ends |
408 | | pub struct Seq<'w, 'k, W: Write> { |
409 | | ser: ContentSerializer<'w, 'k, W>, |
410 | | /// Classification of the result of the last serialized element. |
411 | | last: WriteResult, |
412 | | } |
413 | | |
414 | | impl<'w, 'i, W: Write> SerializeSeq for Seq<'w, 'i, W> { |
415 | | type Ok = WriteResult; |
416 | | type Error = SeError; |
417 | | |
418 | 0 | fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error> |
419 | 0 | where |
420 | 0 | T: ?Sized + Serialize, |
421 | | { |
422 | 0 | self.last = value.serialize(self.ser.new_seq_element_serializer(self.last.is_text()))?; |
423 | | // Write indent for next element if indents are used |
424 | 0 | self.ser.write_indent = self.last.allow_indent(); |
425 | 0 | Ok(()) |
426 | 0 | } |
427 | | |
428 | | #[inline] |
429 | 0 | fn end(self) -> Result<Self::Ok, Self::Error> { |
430 | 0 | Ok(self.last) |
431 | 0 | } |
432 | | } |
433 | | |
434 | | impl<'w, 'i, W: Write> SerializeTuple for Seq<'w, 'i, W> { |
435 | | type Ok = WriteResult; |
436 | | type Error = SeError; |
437 | | |
438 | | #[inline] |
439 | 0 | fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error> |
440 | 0 | where |
441 | 0 | T: ?Sized + Serialize, |
442 | | { |
443 | 0 | SerializeSeq::serialize_element(self, value) |
444 | 0 | } |
445 | | |
446 | | #[inline] |
447 | 0 | fn end(self) -> Result<Self::Ok, Self::Error> { |
448 | 0 | SerializeSeq::end(self) |
449 | 0 | } |
450 | | } |
451 | | |
452 | | impl<'w, 'i, W: Write> SerializeTupleStruct for Seq<'w, 'i, W> { |
453 | | type Ok = WriteResult; |
454 | | type Error = SeError; |
455 | | |
456 | | #[inline] |
457 | 0 | fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error> |
458 | 0 | where |
459 | 0 | T: ?Sized + Serialize, |
460 | | { |
461 | 0 | SerializeSeq::serialize_element(self, value) |
462 | 0 | } |
463 | | |
464 | | #[inline] |
465 | 0 | fn end(self) -> Result<Self::Ok, Self::Error> { |
466 | 0 | SerializeSeq::end(self) |
467 | 0 | } |
468 | | } |
469 | | |
470 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
471 | | |
472 | | /// Make tests public to reuse types in `elements::tests` module |
473 | | #[cfg(test)] |
474 | | pub(super) mod tests { |
475 | | use super::*; |
476 | | use crate::utils::Bytes; |
477 | | use serde::Serialize; |
478 | | use std::collections::BTreeMap; |
479 | | use WriteResult::*; |
480 | | |
481 | | #[derive(Debug, Serialize, PartialEq)] |
482 | | pub struct Unit; |
483 | | |
484 | | #[derive(Debug, Serialize, PartialEq)] |
485 | | #[serde(rename = "<\"&'>")] |
486 | | pub struct UnitEscaped; |
487 | | |
488 | | #[derive(Debug, Serialize, PartialEq)] |
489 | | pub struct Newtype(pub usize); |
490 | | |
491 | | #[derive(Debug, Serialize, PartialEq)] |
492 | | pub struct Tuple(pub &'static str, pub usize); |
493 | | |
494 | | #[derive(Debug, Serialize, PartialEq)] |
495 | | pub struct Struct { |
496 | | pub key: &'static str, |
497 | | pub val: (usize, usize), |
498 | | } |
499 | | |
500 | | /// Struct with a special `$text` field |
501 | | #[derive(Debug, Serialize, PartialEq)] |
502 | | pub struct Text<T> { |
503 | | pub before: &'static str, |
504 | | #[serde(rename = "$text")] |
505 | | pub content: T, |
506 | | pub after: &'static str, |
507 | | } |
508 | | |
509 | | /// Struct with a special `$value` field |
510 | | #[derive(Debug, Serialize, PartialEq)] |
511 | | pub struct Value<T> { |
512 | | pub before: &'static str, |
513 | | #[serde(rename = "$value")] |
514 | | pub content: T, |
515 | | pub after: &'static str, |
516 | | } |
517 | | |
518 | | /// Attributes identified by starting with `@` character |
519 | | #[derive(Debug, Serialize, PartialEq)] |
520 | | pub struct Attributes { |
521 | | #[serde(rename = "@key")] |
522 | | pub key: &'static str, |
523 | | #[serde(rename = "@val")] |
524 | | pub val: (usize, usize), |
525 | | } |
526 | | #[derive(Debug, Serialize, PartialEq)] |
527 | | pub struct AttributesBefore { |
528 | | #[serde(rename = "@key")] |
529 | | pub key: &'static str, |
530 | | pub val: usize, |
531 | | } |
532 | | #[derive(Debug, Serialize, PartialEq)] |
533 | | pub struct AttributesAfter { |
534 | | pub key: &'static str, |
535 | | #[serde(rename = "@val")] |
536 | | pub val: usize, |
537 | | } |
538 | | |
539 | | #[derive(Debug, Serialize, PartialEq)] |
540 | | pub enum Enum { |
541 | | Unit, |
542 | | /// Variant name becomes a tag name, but the name of variant is invalid |
543 | | /// XML name. Serialization of this element should be forbidden |
544 | | #[serde(rename = "<\"&'>")] |
545 | | UnitEscaped, |
546 | | Newtype(usize), |
547 | | Tuple(&'static str, usize), |
548 | | Struct { |
549 | | key: &'static str, |
550 | | /// Should be serialized as elements |
551 | | val: (usize, usize), |
552 | | }, |
553 | | Attributes { |
554 | | #[serde(rename = "@key")] |
555 | | key: &'static str, |
556 | | #[serde(rename = "@val")] |
557 | | val: (usize, usize), |
558 | | }, |
559 | | AttributesBefore { |
560 | | #[serde(rename = "@key")] |
561 | | key: &'static str, |
562 | | val: usize, |
563 | | }, |
564 | | AttributesAfter { |
565 | | key: &'static str, |
566 | | #[serde(rename = "@val")] |
567 | | val: usize, |
568 | | }, |
569 | | } |
570 | | |
571 | | #[derive(Debug, Serialize, PartialEq)] |
572 | | pub enum SpecialEnum<T> { |
573 | | /// Struct variant with a special `$text` field |
574 | | Text { |
575 | | before: &'static str, |
576 | | #[serde(rename = "$text")] |
577 | | content: T, |
578 | | after: &'static str, |
579 | | }, |
580 | | /// Struct variant with a special `$value` field |
581 | | Value { |
582 | | before: &'static str, |
583 | | #[serde(rename = "$value")] |
584 | | content: T, |
585 | | after: &'static str, |
586 | | }, |
587 | | } |
588 | | |
589 | | mod without_indent { |
590 | | use super::Struct; |
591 | | use super::*; |
592 | | use pretty_assertions::assert_eq; |
593 | | |
594 | | /// Checks that given `$data` successfully serialized as `$expected` |
595 | | macro_rules! serialize_as { |
596 | | ($name:ident: $data:expr => $expected:expr) => { |
597 | | serialize_as!($name: $data => $expected, WriteResult::Element); |
598 | | }; |
599 | | ($name:ident: $data:expr => $expected:expr, $result:expr) => { |
600 | | #[test] |
601 | | fn $name() { |
602 | | let mut buffer = String::new(); |
603 | | let ser = ContentSerializer { |
604 | | writer: &mut buffer, |
605 | | level: QuoteLevel::Full, |
606 | | indent: Indent::None, |
607 | | write_indent: false, |
608 | | text_format: TextFormat::Text, |
609 | | allow_primitive: true, |
610 | | expand_empty_elements: false, |
611 | | }; |
612 | | |
613 | | let result = $data.serialize(ser).unwrap(); |
614 | | assert_eq!(buffer, $expected); |
615 | | assert_eq!(result, $result); |
616 | | } |
617 | | }; |
618 | | } |
619 | | |
620 | | /// Checks that attempt to serialize given `$data` results to a |
621 | | /// serialization error `$kind` with `$reason` |
622 | | macro_rules! err { |
623 | | ($name:ident: $data:expr => $kind:ident($reason:literal)) => { |
624 | | #[test] |
625 | | fn $name() { |
626 | | let mut buffer = String::new(); |
627 | | let ser = ContentSerializer { |
628 | | writer: &mut buffer, |
629 | | level: QuoteLevel::Full, |
630 | | indent: Indent::None, |
631 | | write_indent: false, |
632 | | text_format: TextFormat::Text, |
633 | | allow_primitive: true, |
634 | | expand_empty_elements: false, |
635 | | }; |
636 | | |
637 | | match $data.serialize(ser).unwrap_err() { |
638 | | SeError::$kind(e) => assert_eq!(e, $reason), |
639 | | e => panic!( |
640 | | "Expected `Err({}({}))`, but got `{:?}`", |
641 | | stringify!($kind), |
642 | | $reason, |
643 | | e |
644 | | ), |
645 | | } |
646 | | // We could write something before fail |
647 | | // assert_eq!(buffer, ""); |
648 | | } |
649 | | }; |
650 | | } |
651 | | |
652 | | // Primitives is serialized in the same way as for SimpleTypeSerializer |
653 | | serialize_as!(false_: false => "false", Text); |
654 | | serialize_as!(true_: true => "true", Text); |
655 | | |
656 | | serialize_as!(i8_: -42i8 => "-42", Text); |
657 | | serialize_as!(i16_: -4200i16 => "-4200", Text); |
658 | | serialize_as!(i32_: -42000000i32 => "-42000000", Text); |
659 | | serialize_as!(i64_: -42000000000000i64 => "-42000000000000", Text); |
660 | | serialize_as!(isize_: -42000000isize => "-42000000", Text); |
661 | | |
662 | | serialize_as!(u8_: 42u8 => "42", Text); |
663 | | serialize_as!(u16_: 4200u16 => "4200", Text); |
664 | | serialize_as!(u32_: 42000000u32 => "42000000", Text); |
665 | | serialize_as!(u64_: 42000000000000u64 => "42000000000000", Text); |
666 | | serialize_as!(usize_: 42000000usize => "42000000", Text); |
667 | | |
668 | | serde_if_integer128! { |
669 | | serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000", Text); |
670 | | serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000", Text); |
671 | | } |
672 | | |
673 | | serialize_as!(f32_: 4.2f32 => "4.2", Text); |
674 | | serialize_as!(f64_: 4.2f64 => "4.2", Text); |
675 | | |
676 | | serialize_as!(char_non_escaped: 'h' => "h", SensitiveText); |
677 | | serialize_as!(char_lt: '<' => "<", SensitiveText); |
678 | | serialize_as!(char_gt: '>' => ">", SensitiveText); |
679 | | serialize_as!(char_amp: '&' => "&", SensitiveText); |
680 | | serialize_as!(char_apos: '\'' => "'", SensitiveText); |
681 | | serialize_as!(char_quot: '"' => """, SensitiveText); |
682 | | serialize_as!(char_space: ' ' => " ", SensitiveText); |
683 | | |
684 | | serialize_as!(str_non_escaped: "non-escaped string" => "non-escaped string", SensitiveText); |
685 | | serialize_as!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>", SensitiveText); |
686 | | |
687 | | err!(bytes: Bytes(b"<\"escaped & bytes'>") => Unsupported("`serialize_bytes` not supported yet")); |
688 | | |
689 | | serialize_as!(option_none: Option::<Enum>::None => "", SensitiveNothing); |
690 | | serialize_as!(option_some: Some("non-escaped string") => "non-escaped string", SensitiveText); |
691 | | serialize_as!(option_some_empty_str: Some("") => "", SensitiveText); |
692 | | |
693 | | serialize_as!(unit: () => "", Nothing); |
694 | | serialize_as!(unit_struct: Unit => "", Nothing); |
695 | | serialize_as!(unit_struct_escaped: UnitEscaped => "", Nothing); |
696 | | |
697 | | // Unlike SimpleTypeSerializer, enumeration values serialized as tags |
698 | | serialize_as!(enum_unit: Enum::Unit => "<Unit/>"); |
699 | | err!(enum_unit_escaped: Enum::UnitEscaped |
700 | | => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`")); |
701 | | |
702 | | // Newtypes recursively applies ContentSerializer |
703 | | serialize_as!(newtype: Newtype(42) => "42", Text); |
704 | | serialize_as!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>"); |
705 | | |
706 | | // Note that sequences of primitives serialized without delimiters! |
707 | | err!(seq: vec![1, 2, 3] |
708 | | => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); |
709 | | serialize_as!(seq_empty: Vec::<usize>::new() => "", SensitiveNothing); |
710 | | err!(tuple: ("<\"&'>", "with\t\r\n spaces", 3usize) |
711 | | => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); |
712 | | err!(tuple_struct: Tuple("first", 42) |
713 | | => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); |
714 | | serialize_as!(enum_tuple: Enum::Tuple("first", 42) |
715 | | => "<Tuple>first</Tuple>\ |
716 | | <Tuple>42</Tuple>"); |
717 | | |
718 | | // Structured types cannot be serialized without surrounding tag, which |
719 | | // only `enum` can provide |
720 | | err!(map: BTreeMap::from([("_1", 2), ("_3", 4)]) |
721 | | => Unsupported("serialization of map types is not supported in `$value` field")); |
722 | | serialize_as!(struct_: Struct { key: "answer", val: (42, 42) } |
723 | | => "<Struct>\ |
724 | | <key>answer</key>\ |
725 | | <val>42</val>\ |
726 | | <val>42</val>\ |
727 | | </Struct>"); |
728 | | |
729 | | serialize_as!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) } |
730 | | => "<Struct>\ |
731 | | <key>answer</key>\ |
732 | | <val>42</val>\ |
733 | | <val>42</val>\ |
734 | | </Struct>"); |
735 | | |
736 | | /// Special field name `$text` should be serialized as a text content |
737 | | mod text_field { |
738 | | use super::*; |
739 | | use pretty_assertions::assert_eq; |
740 | | |
741 | | err!(map: BTreeMap::from([("$text", 2), ("_3", 4)]) |
742 | | => Unsupported("serialization of map types is not supported in `$value` field")); |
743 | | serialize_as!(struct_: |
744 | | Text { |
745 | | before: "answer", |
746 | | content: (42, 42), |
747 | | after: "answer", |
748 | | } |
749 | | => "<Text>\ |
750 | | <before>answer</before>\ |
751 | | 42 42\ |
752 | | <after>answer</after>\ |
753 | | </Text>"); |
754 | | serialize_as!(enum_struct: |
755 | | SpecialEnum::Text { |
756 | | before: "answer", |
757 | | content: (42, 42), |
758 | | after: "answer", |
759 | | } |
760 | | => "<Text>\ |
761 | | <before>answer</before>\ |
762 | | 42 42\ |
763 | | <after>answer</after>\ |
764 | | </Text>"); |
765 | | } |
766 | | |
767 | | /// `$text` field inside a struct variant of an enum |
768 | | mod enum_with_text_field { |
769 | | use super::*; |
770 | | use pretty_assertions::assert_eq; |
771 | | |
772 | | macro_rules! text { |
773 | | ($name:ident: $data:expr => $expected:literal) => { |
774 | | serialize_as!($name: |
775 | | SpecialEnum::Text { |
776 | | before: "answer", |
777 | | content: $data, |
778 | | after: "answer", |
779 | | } |
780 | | => concat!( |
781 | | "<Text><before>answer</before>", |
782 | | $expected, |
783 | | "<after>answer</after></Text>", |
784 | | )); |
785 | | }; |
786 | | } |
787 | | |
788 | | text!(false_: false => "false"); |
789 | | text!(true_: true => "true"); |
790 | | |
791 | | text!(i8_: -42i8 => "-42"); |
792 | | text!(i16_: -4200i16 => "-4200"); |
793 | | text!(i32_: -42000000i32 => "-42000000"); |
794 | | text!(i64_: -42000000000000i64 => "-42000000000000"); |
795 | | text!(isize_: -42000000isize => "-42000000"); |
796 | | |
797 | | text!(u8_: 42u8 => "42"); |
798 | | text!(u16_: 4200u16 => "4200"); |
799 | | text!(u32_: 42000000u32 => "42000000"); |
800 | | text!(u64_: 42000000000000u64 => "42000000000000"); |
801 | | text!(usize_: 42000000usize => "42000000"); |
802 | | |
803 | | serde_if_integer128! { |
804 | | text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000"); |
805 | | text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000"); |
806 | | } |
807 | | |
808 | | text!(f32_: 4.2f32 => "4.2"); |
809 | | text!(f64_: 4.2f64 => "4.2"); |
810 | | |
811 | | text!(char_non_escaped: 'h' => "h"); |
812 | | text!(char_lt: '<' => "<"); |
813 | | text!(char_gt: '>' => ">"); |
814 | | text!(char_amp: '&' => "&"); |
815 | | text!(char_apos: '\'' => "'"); |
816 | | text!(char_quot: '"' => """); |
817 | | text!(char_space: ' ' => " "); |
818 | | |
819 | | text!(str_non_escaped: "non-escaped string" => "non-escaped string"); |
820 | | text!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); |
821 | | |
822 | | err!(bytes: |
823 | | SpecialEnum::Text { |
824 | | before: "answer", |
825 | | content: Bytes(b"<\"escaped & bytes'>"), |
826 | | after: "answer", |
827 | | } |
828 | | => Unsupported("`serialize_bytes` not supported yet")); |
829 | | |
830 | | text!(option_none: Option::<&str>::None => ""); |
831 | | text!(option_some: Some("non-escaped string") => "non-escaped string"); |
832 | | text!(option_some_empty_str: Some("") => ""); |
833 | | |
834 | | text!(unit: () => ""); |
835 | | text!(unit_struct: Unit => ""); |
836 | | text!(unit_struct_escaped: UnitEscaped => ""); |
837 | | |
838 | | text!(enum_unit: Enum::Unit => "Unit"); |
839 | | text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>"); |
840 | | |
841 | | text!(newtype: Newtype(42) => "42"); |
842 | | // We have no space where name of a variant can be stored |
843 | | err!(enum_newtype: |
844 | | SpecialEnum::Text { |
845 | | before: "answer", |
846 | | content: Enum::Newtype(42), |
847 | | after: "answer", |
848 | | } |
849 | | => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as text content value")); |
850 | | |
851 | | // Sequences are serialized separated by spaces, all spaces inside are escaped |
852 | | text!(seq: vec![1, 2, 3] => "1 2 3"); |
853 | | text!(seq_empty: Vec::<usize>::new() => ""); |
854 | | text!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize) |
855 | | => "<"&'> \ |
856 | | with	  spaces \ |
857 | | 3"); |
858 | | text!(tuple_struct: Tuple("first", 42) => "first 42"); |
859 | | // We have no space where name of a variant can be stored |
860 | | err!(enum_tuple: |
861 | | SpecialEnum::Text { |
862 | | before: "answer", |
863 | | content: Enum::Tuple("first", 42), |
864 | | after: "answer", |
865 | | } |
866 | | => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value")); |
867 | | |
868 | | // Complex types cannot be serialized in `$text` field |
869 | | err!(map: |
870 | | SpecialEnum::Text { |
871 | | before: "answer", |
872 | | content: BTreeMap::from([("_1", 2), ("_3", 4)]), |
873 | | after: "answer", |
874 | | } |
875 | | => Unsupported("cannot serialize map as text content value")); |
876 | | err!(struct_: |
877 | | SpecialEnum::Text { |
878 | | before: "answer", |
879 | | content: Struct { key: "answer", val: (42, 42) }, |
880 | | after: "answer", |
881 | | } |
882 | | => Unsupported("cannot serialize struct `Struct` as text content value")); |
883 | | err!(enum_struct: |
884 | | SpecialEnum::Text { |
885 | | before: "answer", |
886 | | content: Enum::Struct { key: "answer", val: (42, 42) }, |
887 | | after: "answer", |
888 | | } |
889 | | => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value")); |
890 | | } |
891 | | |
892 | | /// `$value` field inside a struct variant of an enum |
893 | | mod enum_with_value_field { |
894 | | use super::*; |
895 | | use pretty_assertions::assert_eq; |
896 | | |
897 | | macro_rules! value { |
898 | | ($name:ident: $data:expr => $expected:literal) => { |
899 | | serialize_as!($name: |
900 | | SpecialEnum::Value { |
901 | | before: "answer", |
902 | | content: $data, |
903 | | after: "answer", |
904 | | } |
905 | | => concat!( |
906 | | "<Value><before>answer</before>", |
907 | | $expected, |
908 | | "<after>answer</after></Value>", |
909 | | )); |
910 | | }; |
911 | | } |
912 | | |
913 | | value!(false_: false => "false"); |
914 | | value!(true_: true => "true"); |
915 | | |
916 | | value!(i8_: -42i8 => "-42"); |
917 | | value!(i16_: -4200i16 => "-4200"); |
918 | | value!(i32_: -42000000i32 => "-42000000"); |
919 | | value!(i64_: -42000000000000i64 => "-42000000000000"); |
920 | | value!(isize_: -42000000isize => "-42000000"); |
921 | | |
922 | | value!(u8_: 42u8 => "42"); |
923 | | value!(u16_: 4200u16 => "4200"); |
924 | | value!(u32_: 42000000u32 => "42000000"); |
925 | | value!(u64_: 42000000000000u64 => "42000000000000"); |
926 | | value!(usize_: 42000000usize => "42000000"); |
927 | | |
928 | | serde_if_integer128! { |
929 | | value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000"); |
930 | | value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000"); |
931 | | } |
932 | | |
933 | | value!(f32_: 4.2f32 => "4.2"); |
934 | | value!(f64_: 4.2f64 => "4.2"); |
935 | | |
936 | | value!(char_non_escaped: 'h' => "h"); |
937 | | value!(char_lt: '<' => "<"); |
938 | | value!(char_gt: '>' => ">"); |
939 | | value!(char_amp: '&' => "&"); |
940 | | value!(char_apos: '\'' => "'"); |
941 | | value!(char_quot: '"' => """); |
942 | | value!(char_space: ' ' => " "); |
943 | | |
944 | | value!(str_non_escaped: "non-escaped string" => "non-escaped string"); |
945 | | value!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); |
946 | | |
947 | | err!(bytes: |
948 | | SpecialEnum::Value { |
949 | | before: "answer", |
950 | | content: Bytes(b"<\"escaped & bytes'>"), |
951 | | after: "answer", |
952 | | } |
953 | | => Unsupported("`serialize_bytes` not supported yet")); |
954 | | |
955 | | value!(option_none: Option::<&str>::None => ""); |
956 | | value!(option_some: Some("non-escaped string") => "non-escaped string"); |
957 | | value!(option_some_empty_str: Some("") => ""); |
958 | | |
959 | | value!(unit: () => ""); |
960 | | value!(unit_struct: Unit => ""); |
961 | | value!(unit_struct_escaped: UnitEscaped => ""); |
962 | | |
963 | | value!(enum_unit: Enum::Unit => "<Unit/>"); |
964 | | err!(enum_unit_escaped: |
965 | | SpecialEnum::Value { |
966 | | before: "answer", |
967 | | content: Enum::UnitEscaped, |
968 | | after: "answer", |
969 | | } |
970 | | => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`")); |
971 | | |
972 | | value!(newtype: Newtype(42) => "42"); |
973 | | value!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>"); |
974 | | |
975 | | // Note that sequences of primitives serialized without delimiters! |
976 | | err!(seq: |
977 | | SpecialEnum::Value { |
978 | | before: "answer", |
979 | | content: vec![1, 2, 3], |
980 | | after: "answer", |
981 | | } |
982 | | => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); |
983 | | value!(seq_empty: Vec::<usize>::new() => ""); |
984 | | err!(tuple: |
985 | | SpecialEnum::Value { |
986 | | before: "answer", |
987 | | content: ("<\"&'>", "with\t\n\r spaces", 3usize), |
988 | | after: "answer", |
989 | | } |
990 | | => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); |
991 | | err!(tuple_struct: |
992 | | SpecialEnum::Value { |
993 | | before: "answer", |
994 | | content: Tuple("first", 42), |
995 | | after: "answer", |
996 | | } |
997 | | => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); |
998 | | value!(enum_tuple: Enum::Tuple("first", 42) |
999 | | => "<Tuple>first</Tuple>\ |
1000 | | <Tuple>42</Tuple>"); |
1001 | | |
1002 | | // We cannot wrap map or struct in any container and should not |
1003 | | // flatten it, so it is impossible to serialize maps and structs |
1004 | | err!(map: |
1005 | | SpecialEnum::Value { |
1006 | | before: "answer", |
1007 | | content: BTreeMap::from([("_1", 2), ("_3", 4)]), |
1008 | | after: "answer", |
1009 | | } |
1010 | | => Unsupported("serialization of map types is not supported in `$value` field")); |
1011 | | value!(struct_: |
1012 | | SpecialEnum::Value { |
1013 | | before: "answer", |
1014 | | content: Struct { key: "answer", val: (42, 42) }, |
1015 | | after: "answer", |
1016 | | } |
1017 | | => "<Value>\ |
1018 | | <before>answer</before>\ |
1019 | | <Struct>\ |
1020 | | <key>answer</key>\ |
1021 | | <val>42</val>\ |
1022 | | <val>42</val>\ |
1023 | | </Struct>\ |
1024 | | <after>answer</after>\ |
1025 | | </Value>"); |
1026 | | value!(enum_struct: |
1027 | | Enum::Struct { key: "answer", val: (42, 42) } |
1028 | | => "<Struct>\ |
1029 | | <key>answer</key>\ |
1030 | | <val>42</val>\ |
1031 | | <val>42</val>\ |
1032 | | </Struct>"); |
1033 | | } |
1034 | | |
1035 | | mod attributes { |
1036 | | use super::*; |
1037 | | use pretty_assertions::assert_eq; |
1038 | | |
1039 | | err!(map_attr: BTreeMap::from([("@key1", 1), ("@key2", 2)]) |
1040 | | => Unsupported("serialization of map types is not supported in `$value` field")); |
1041 | | err!(map_mixed: BTreeMap::from([("@key1", 1), ("key2", 2)]) |
1042 | | => Unsupported("serialization of map types is not supported in `$value` field")); |
1043 | | |
1044 | | serialize_as!(struct_: Attributes { key: "answer", val: (42, 42) } |
1045 | | => r#"<Attributes key="answer" val="42 42"/>"#); |
1046 | | serialize_as!(struct_before: AttributesBefore { key: "answer", val: 42 } |
1047 | | => r#"<AttributesBefore key="answer"><val>42</val></AttributesBefore>"#); |
1048 | | serialize_as!(struct_after: AttributesAfter { key: "answer", val: 42 } |
1049 | | => r#"<AttributesAfter val="42"><key>answer</key></AttributesAfter>"#); |
1050 | | |
1051 | | serialize_as!(enum_: Enum::Attributes { key: "answer", val: (42, 42) } |
1052 | | => r#"<Attributes key="answer" val="42 42"/>"#); |
1053 | | serialize_as!(enum_before: Enum::AttributesBefore { key: "answer", val: 42 } |
1054 | | => r#"<AttributesBefore key="answer"><val>42</val></AttributesBefore>"#); |
1055 | | serialize_as!(enum_after: Enum::AttributesAfter { key: "answer", val: 42 } |
1056 | | => r#"<AttributesAfter val="42"><key>answer</key></AttributesAfter>"#); |
1057 | | } |
1058 | | } |
1059 | | |
1060 | | mod with_indent { |
1061 | | use super::Struct; |
1062 | | use super::*; |
1063 | | use crate::writer::Indentation; |
1064 | | use pretty_assertions::assert_eq; |
1065 | | |
1066 | | /// Checks that given `$data` successfully serialized as `$expected` |
1067 | | macro_rules! serialize_as { |
1068 | | ($name:ident: $data:expr => $expected:expr) => { |
1069 | | serialize_as!($name: $data => $expected, WriteResult::Element); |
1070 | | }; |
1071 | | ($name:ident: $data:expr => $expected:expr, $result:expr) => { |
1072 | | #[test] |
1073 | | fn $name() { |
1074 | | let mut buffer = String::new(); |
1075 | | let ser = ContentSerializer { |
1076 | | writer: &mut buffer, |
1077 | | level: QuoteLevel::Full, |
1078 | | indent: Indent::Owned(Indentation::new(b' ', 2)), |
1079 | | write_indent: false, |
1080 | | text_format: TextFormat::Text, |
1081 | | allow_primitive: true, |
1082 | | expand_empty_elements: false, |
1083 | | }; |
1084 | | |
1085 | | let result = $data.serialize(ser).unwrap(); |
1086 | | assert_eq!(buffer, $expected); |
1087 | | assert_eq!(result, $result); |
1088 | | } |
1089 | | }; |
1090 | | } |
1091 | | |
1092 | | /// Checks that attempt to serialize given `$data` results to a |
1093 | | /// serialization error `$kind` with `$reason` |
1094 | | macro_rules! err { |
1095 | | ($name:ident: $data:expr => $kind:ident($reason:literal)) => { |
1096 | | #[test] |
1097 | | fn $name() { |
1098 | | let mut buffer = String::new(); |
1099 | | let ser = ContentSerializer { |
1100 | | writer: &mut buffer, |
1101 | | level: QuoteLevel::Full, |
1102 | | indent: Indent::Owned(Indentation::new(b' ', 2)), |
1103 | | write_indent: false, |
1104 | | text_format: TextFormat::Text, |
1105 | | allow_primitive: true, |
1106 | | expand_empty_elements: false, |
1107 | | }; |
1108 | | |
1109 | | match $data.serialize(ser).unwrap_err() { |
1110 | | SeError::$kind(e) => assert_eq!(e, $reason), |
1111 | | e => panic!( |
1112 | | "Expected `Err({}({}))`, but got `{:?}`", |
1113 | | stringify!($kind), |
1114 | | $reason, |
1115 | | e |
1116 | | ), |
1117 | | } |
1118 | | // We can write something before fail |
1119 | | // assert_eq!(buffer, ""); |
1120 | | } |
1121 | | }; |
1122 | | } |
1123 | | |
1124 | | serialize_as!(false_: false => "false", Text); |
1125 | | serialize_as!(true_: true => "true", Text); |
1126 | | |
1127 | | serialize_as!(i8_: -42i8 => "-42", Text); |
1128 | | serialize_as!(i16_: -4200i16 => "-4200", Text); |
1129 | | serialize_as!(i32_: -42000000i32 => "-42000000", Text); |
1130 | | serialize_as!(i64_: -42000000000000i64 => "-42000000000000", Text); |
1131 | | serialize_as!(isize_: -42000000isize => "-42000000", Text); |
1132 | | |
1133 | | serialize_as!(u8_: 42u8 => "42", Text); |
1134 | | serialize_as!(u16_: 4200u16 => "4200", Text); |
1135 | | serialize_as!(u32_: 42000000u32 => "42000000", Text); |
1136 | | serialize_as!(u64_: 42000000000000u64 => "42000000000000", Text); |
1137 | | serialize_as!(usize_: 42000000usize => "42000000", Text); |
1138 | | |
1139 | | serde_if_integer128! { |
1140 | | serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000", Text); |
1141 | | serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000", Text); |
1142 | | } |
1143 | | |
1144 | | serialize_as!(f32_: 4.2f32 => "4.2", Text); |
1145 | | serialize_as!(f64_: 4.2f64 => "4.2", Text); |
1146 | | |
1147 | | serialize_as!(char_non_escaped: 'h' => "h", SensitiveText); |
1148 | | serialize_as!(char_lt: '<' => "<", SensitiveText); |
1149 | | serialize_as!(char_gt: '>' => ">", SensitiveText); |
1150 | | serialize_as!(char_amp: '&' => "&", SensitiveText); |
1151 | | serialize_as!(char_apos: '\'' => "'", SensitiveText); |
1152 | | serialize_as!(char_quot: '"' => """, SensitiveText); |
1153 | | serialize_as!(char_space: ' ' => " ", SensitiveText); |
1154 | | |
1155 | | serialize_as!(str_non_escaped: "non-escaped string" => "non-escaped string", SensitiveText); |
1156 | | serialize_as!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>", SensitiveText); |
1157 | | |
1158 | | err!(bytes: Bytes(b"<\"escaped & bytes'>") => Unsupported("`serialize_bytes` not supported yet")); |
1159 | | |
1160 | | serialize_as!(option_none: Option::<Enum>::None => "", SensitiveNothing); |
1161 | | serialize_as!(option_some: Some(Enum::Unit) => "<Unit/>"); |
1162 | | |
1163 | | serialize_as!(unit: () => "", Nothing); |
1164 | | serialize_as!(unit_struct: Unit => "", Nothing); |
1165 | | serialize_as!(unit_struct_escaped: UnitEscaped => "", Nothing); |
1166 | | |
1167 | | // Unlike SimpleTypeSerializer, enumeration values serialized as tags |
1168 | | serialize_as!(enum_unit: Enum::Unit => "<Unit/>"); |
1169 | | err!(enum_unit_escaped: Enum::UnitEscaped |
1170 | | => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`")); |
1171 | | |
1172 | | // Newtypes recursively applies ContentSerializer |
1173 | | serialize_as!(newtype: Newtype(42) => "42", Text); |
1174 | | serialize_as!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>"); |
1175 | | |
1176 | | err!(seq: vec![1, 2, 3] |
1177 | | => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); |
1178 | | serialize_as!(seq_empty: Vec::<usize>::new() => "", SensitiveNothing); |
1179 | | err!(tuple: ("<\"&'>", "with\t\r\n spaces", 3usize) |
1180 | | => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); |
1181 | | err!(tuple_struct: Tuple("first", 42) |
1182 | | => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); |
1183 | | serialize_as!(enum_tuple: Enum::Tuple("first", 42) |
1184 | | => "<Tuple>first</Tuple>\n\ |
1185 | | <Tuple>42</Tuple>"); |
1186 | | |
1187 | | // Structured types cannot be serialized without surrounding tag, which |
1188 | | // only `enum` can provide |
1189 | | err!(map: BTreeMap::from([("_1", 2), ("_3", 4)]) |
1190 | | => Unsupported("serialization of map types is not supported in `$value` field")); |
1191 | | serialize_as!(struct_: Struct { key: "answer", val: (42, 42) } |
1192 | | => "<Struct>\n \ |
1193 | | <key>answer</key>\n \ |
1194 | | <val>42</val>\n \ |
1195 | | <val>42</val>\n\ |
1196 | | </Struct>"); |
1197 | | serialize_as!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) } |
1198 | | => "<Struct>\n \ |
1199 | | <key>answer</key>\n \ |
1200 | | <val>42</val>\n \ |
1201 | | <val>42</val>\n\ |
1202 | | </Struct>"); |
1203 | | |
1204 | | /// Special field name `$text` should be serialized as text content |
1205 | | mod text_field { |
1206 | | use super::*; |
1207 | | use pretty_assertions::assert_eq; |
1208 | | |
1209 | | err!(map: BTreeMap::from([("$text", 2), ("_3", 4)]) |
1210 | | => Unsupported("serialization of map types is not supported in `$value` field")); |
1211 | | serialize_as!(struct_: |
1212 | | Text { |
1213 | | before: "answer", |
1214 | | content: (42, 42), |
1215 | | after: "answer", |
1216 | | } |
1217 | | => "<Text>\n \ |
1218 | | <before>answer</before>42 42<after>answer</after>\n\ |
1219 | | </Text>"); |
1220 | | serialize_as!(enum_struct: |
1221 | | SpecialEnum::Text { |
1222 | | before: "answer", |
1223 | | content: (42, 42), |
1224 | | after: "answer", |
1225 | | } |
1226 | | => "<Text>\n \ |
1227 | | <before>answer</before>42 42<after>answer</after>\n\ |
1228 | | </Text>"); |
1229 | | } |
1230 | | |
1231 | | /// `$text` field inside a struct variant of an enum |
1232 | | mod enum_with_text_field { |
1233 | | use super::*; |
1234 | | use pretty_assertions::assert_eq; |
1235 | | |
1236 | | macro_rules! text { |
1237 | | ($name:ident: $data:expr => $expected:literal) => { |
1238 | | serialize_as!($name: |
1239 | | SpecialEnum::Text { |
1240 | | before: "answer", |
1241 | | content: $data, |
1242 | | after: "answer", |
1243 | | } |
1244 | | => concat!( |
1245 | | "<Text>\n <before>answer</before>", |
1246 | | $expected, |
1247 | | "<after>answer</after>\n</Text>", |
1248 | | )); |
1249 | | }; |
1250 | | } |
1251 | | |
1252 | | text!(false_: false => "false"); |
1253 | | text!(true_: true => "true"); |
1254 | | |
1255 | | text!(i8_: -42i8 => "-42"); |
1256 | | text!(i16_: -4200i16 => "-4200"); |
1257 | | text!(i32_: -42000000i32 => "-42000000"); |
1258 | | text!(i64_: -42000000000000i64 => "-42000000000000"); |
1259 | | text!(isize_: -42000000isize => "-42000000"); |
1260 | | |
1261 | | text!(u8_: 42u8 => "42"); |
1262 | | text!(u16_: 4200u16 => "4200"); |
1263 | | text!(u32_: 42000000u32 => "42000000"); |
1264 | | text!(u64_: 42000000000000u64 => "42000000000000"); |
1265 | | text!(usize_: 42000000usize => "42000000"); |
1266 | | |
1267 | | serde_if_integer128! { |
1268 | | text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000"); |
1269 | | text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000"); |
1270 | | } |
1271 | | |
1272 | | text!(f32_: 4.2f32 => "4.2"); |
1273 | | text!(f64_: 4.2f64 => "4.2"); |
1274 | | |
1275 | | text!(char_non_escaped: 'h' => "h"); |
1276 | | text!(char_lt: '<' => "<"); |
1277 | | text!(char_gt: '>' => ">"); |
1278 | | text!(char_amp: '&' => "&"); |
1279 | | text!(char_apos: '\'' => "'"); |
1280 | | text!(char_quot: '"' => """); |
1281 | | text!(char_space: ' ' => " "); |
1282 | | |
1283 | | text!(str_non_escaped: "non-escaped string" => "non-escaped string"); |
1284 | | text!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); |
1285 | | |
1286 | | err!(bytes: |
1287 | | SpecialEnum::Text { |
1288 | | before: "answer", |
1289 | | content: Bytes(b"<\"escaped & bytes'>"), |
1290 | | after: "answer", |
1291 | | } |
1292 | | => Unsupported("`serialize_bytes` not supported yet")); |
1293 | | |
1294 | | text!(option_none: Option::<&str>::None => ""); |
1295 | | text!(option_some: Some("non-escaped string") => "non-escaped string"); |
1296 | | text!(option_some_empty_str: Some("") => ""); |
1297 | | |
1298 | | text!(unit: () => ""); |
1299 | | text!(unit_struct: Unit => ""); |
1300 | | text!(unit_struct_escaped: UnitEscaped => ""); |
1301 | | |
1302 | | text!(enum_unit: Enum::Unit => "Unit"); |
1303 | | text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>"); |
1304 | | |
1305 | | text!(newtype: Newtype(42) => "42"); |
1306 | | // We have no space where name of a variant can be stored |
1307 | | err!(enum_newtype: |
1308 | | SpecialEnum::Text { |
1309 | | before: "answer", |
1310 | | content: Enum::Newtype(42), |
1311 | | after: "answer", |
1312 | | } |
1313 | | => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as text content value")); |
1314 | | |
1315 | | // Sequences are serialized separated by spaces, all spaces inside are escaped |
1316 | | text!(seq: vec![1, 2, 3] => "1 2 3"); |
1317 | | text!(seq_empty: Vec::<usize>::new() => ""); |
1318 | | text!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize) |
1319 | | => "<"&'> \ |
1320 | | with	  spaces \ |
1321 | | 3"); |
1322 | | text!(tuple_struct: Tuple("first", 42) => "first 42"); |
1323 | | // We have no space where name of a variant can be stored |
1324 | | err!(enum_tuple: |
1325 | | SpecialEnum::Text { |
1326 | | before: "answer", |
1327 | | content: Enum::Tuple("first", 42), |
1328 | | after: "answer", |
1329 | | } |
1330 | | => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as text content value")); |
1331 | | |
1332 | | // Complex types cannot be serialized in `$text` field |
1333 | | err!(map: |
1334 | | SpecialEnum::Text { |
1335 | | before: "answer", |
1336 | | content: BTreeMap::from([("_1", 2), ("_3", 4)]), |
1337 | | after: "answer", |
1338 | | } |
1339 | | => Unsupported("cannot serialize map as text content value")); |
1340 | | err!(struct_: |
1341 | | SpecialEnum::Text { |
1342 | | before: "answer", |
1343 | | content: Struct { key: "answer", val: (42, 42) }, |
1344 | | after: "answer", |
1345 | | } |
1346 | | => Unsupported("cannot serialize struct `Struct` as text content value")); |
1347 | | err!(enum_struct: |
1348 | | SpecialEnum::Text { |
1349 | | before: "answer", |
1350 | | content: Enum::Struct { key: "answer", val: (42, 42) }, |
1351 | | after: "answer", |
1352 | | } |
1353 | | => Unsupported("cannot serialize enum struct variant `Enum::Struct` as text content value")); |
1354 | | } |
1355 | | |
1356 | | /// `$value` field inside a struct variant of an enum |
1357 | | mod enum_with_value_field { |
1358 | | use super::*; |
1359 | | use pretty_assertions::assert_eq; |
1360 | | |
1361 | | macro_rules! value { |
1362 | | ($name:ident: $data:expr => $expected:literal) => { |
1363 | | serialize_as!($name: |
1364 | | SpecialEnum::Value { |
1365 | | before: "answer", |
1366 | | content: $data, |
1367 | | after: "answer", |
1368 | | } |
1369 | | => concat!( |
1370 | | "<Value>\n <before>answer</before>", |
1371 | | $expected, |
1372 | | "<after>answer</after>\n</Value>", |
1373 | | )); |
1374 | | }; |
1375 | | } |
1376 | | |
1377 | | value!(false_: false => "false"); |
1378 | | value!(true_: true => "true"); |
1379 | | |
1380 | | value!(i8_: -42i8 => "-42"); |
1381 | | value!(i16_: -4200i16 => "-4200"); |
1382 | | value!(i32_: -42000000i32 => "-42000000"); |
1383 | | value!(i64_: -42000000000000i64 => "-42000000000000"); |
1384 | | value!(isize_: -42000000isize => "-42000000"); |
1385 | | |
1386 | | value!(u8_: 42u8 => "42"); |
1387 | | value!(u16_: 4200u16 => "4200"); |
1388 | | value!(u32_: 42000000u32 => "42000000"); |
1389 | | value!(u64_: 42000000000000u64 => "42000000000000"); |
1390 | | value!(usize_: 42000000usize => "42000000"); |
1391 | | |
1392 | | serde_if_integer128! { |
1393 | | value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000"); |
1394 | | value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000"); |
1395 | | } |
1396 | | |
1397 | | value!(f32_: 4.2f32 => "4.2"); |
1398 | | value!(f64_: 4.2f64 => "4.2"); |
1399 | | |
1400 | | value!(char_non_escaped: 'h' => "h"); |
1401 | | value!(char_lt: '<' => "<"); |
1402 | | value!(char_gt: '>' => ">"); |
1403 | | value!(char_amp: '&' => "&"); |
1404 | | value!(char_apos: '\'' => "'"); |
1405 | | value!(char_quot: '"' => """); |
1406 | | value!(char_space: ' ' => " "); |
1407 | | |
1408 | | value!(str_non_escaped: "non-escaped string" => "non-escaped string"); |
1409 | | value!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>"); |
1410 | | |
1411 | | err!(bytes: |
1412 | | SpecialEnum::Value { |
1413 | | before: "answer", |
1414 | | content: Bytes(b"<\"escaped & bytes'>"), |
1415 | | after: "answer", |
1416 | | } |
1417 | | => Unsupported("`serialize_bytes` not supported yet")); |
1418 | | |
1419 | | value!(option_none: Option::<&str>::None => ""); |
1420 | | value!(option_some: Some("non-escaped string") => "non-escaped string"); |
1421 | | value!(option_some_empty_str: Some("") => ""); |
1422 | | |
1423 | | value!(unit: () => "\n "); |
1424 | | value!(unit_struct: Unit => "\n "); |
1425 | | value!(unit_struct_escaped: UnitEscaped => "\n "); |
1426 | | |
1427 | | value!(enum_unit: Enum::Unit => "\n <Unit/>\n "); |
1428 | | err!(enum_unit_escaped: |
1429 | | SpecialEnum::Value { |
1430 | | before: "answer", |
1431 | | content: Enum::UnitEscaped, |
1432 | | after: "answer", |
1433 | | } |
1434 | | => Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`")); |
1435 | | |
1436 | | value!(newtype: Newtype(42) => "42"); |
1437 | | value!(enum_newtype: Enum::Newtype(42) => "\n <Newtype>42</Newtype>\n "); |
1438 | | |
1439 | | // Note that sequences of primitives serialized without delimiters! |
1440 | | err!(seq: |
1441 | | SpecialEnum::Value { |
1442 | | before: "answer", |
1443 | | content: vec![1, 2, 3], |
1444 | | after: "answer", |
1445 | | } |
1446 | | => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); |
1447 | | value!(seq_empty: Vec::<usize>::new() => ""); |
1448 | | err!(tuple: |
1449 | | SpecialEnum::Value { |
1450 | | before: "answer", |
1451 | | content: ("<\"&'>", "with\t\n\r spaces", 3usize), |
1452 | | after: "answer", |
1453 | | } |
1454 | | => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); |
1455 | | err!(tuple_struct: |
1456 | | SpecialEnum::Value { |
1457 | | before: "answer", |
1458 | | content: Tuple("first", 42), |
1459 | | after: "answer", |
1460 | | } |
1461 | | => Unsupported("consequent primitives would be serialized without delimiter and cannot be deserialized back")); |
1462 | | value!(enum_tuple: Enum::Tuple("first", 42) |
1463 | | => "\n \ |
1464 | | <Tuple>first</Tuple>\n \ |
1465 | | <Tuple>42</Tuple>\n "); |
1466 | | |
1467 | | // We cannot wrap map or struct in any container and should not |
1468 | | // flatten it, so it is impossible to serialize maps and structs |
1469 | | err!(map: |
1470 | | SpecialEnum::Value { |
1471 | | before: "answer", |
1472 | | content: BTreeMap::from([("_1", 2), ("_3", 4)]), |
1473 | | after: "answer", |
1474 | | } |
1475 | | => Unsupported("serialization of map types is not supported in `$value` field")); |
1476 | | value!(struct_: |
1477 | | SpecialEnum::Value { |
1478 | | before: "answer", |
1479 | | content: Struct { key: "answer", val: (42, 42) }, |
1480 | | after: "answer", |
1481 | | } |
1482 | | => "\n \ |
1483 | | <Value>\n \ |
1484 | | <before>answer</before>\n \ |
1485 | | <Struct>\n \ |
1486 | | <key>answer</key>\n \ |
1487 | | <val>42</val>\n \ |
1488 | | <val>42</val>\n \ |
1489 | | </Struct>\n \ |
1490 | | <after>answer</after>\n \ |
1491 | | </Value>\n "); |
1492 | | value!(enum_struct: |
1493 | | Enum::Struct { key: "answer", val: (42, 42) } |
1494 | | => "\n \ |
1495 | | <Struct>\n \ |
1496 | | <key>answer</key>\n \ |
1497 | | <val>42</val>\n \ |
1498 | | <val>42</val>\n \ |
1499 | | </Struct>\n "); |
1500 | | } |
1501 | | |
1502 | | mod attributes { |
1503 | | use super::*; |
1504 | | use pretty_assertions::assert_eq; |
1505 | | |
1506 | | err!(map_attr: BTreeMap::from([("@key1", 1), ("@key2", 2)]) |
1507 | | => Unsupported("serialization of map types is not supported in `$value` field")); |
1508 | | err!(map_mixed: BTreeMap::from([("@key1", 1), ("key2", 2)]) |
1509 | | => Unsupported("serialization of map types is not supported in `$value` field")); |
1510 | | |
1511 | | serialize_as!(struct_: Attributes { key: "answer", val: (42, 42) } |
1512 | | => r#"<Attributes key="answer" val="42 42"/>"#); |
1513 | | serialize_as!(struct_before: AttributesBefore { key: "answer", val: 42 } |
1514 | | => "<AttributesBefore key=\"answer\">\n \ |
1515 | | <val>42</val>\n\ |
1516 | | </AttributesBefore>"); |
1517 | | serialize_as!(struct_after: AttributesAfter { key: "answer", val: 42 } |
1518 | | => "<AttributesAfter val=\"42\">\n \ |
1519 | | <key>answer</key>\n\ |
1520 | | </AttributesAfter>"); |
1521 | | |
1522 | | serialize_as!(enum_: Enum::Attributes { key: "answer", val: (42, 42) } |
1523 | | => r#"<Attributes key="answer" val="42 42"/>"#); |
1524 | | serialize_as!(enum_before: Enum::AttributesBefore { key: "answer", val: 42 } |
1525 | | => "<AttributesBefore key=\"answer\">\n \ |
1526 | | <val>42</val>\n\ |
1527 | | </AttributesBefore>"); |
1528 | | serialize_as!(enum_after: Enum::AttributesAfter { key: "answer", val: 42 } |
1529 | | => "<AttributesAfter val=\"42\">\n \ |
1530 | | <key>answer</key>\n\ |
1531 | | </AttributesAfter>"); |
1532 | | } |
1533 | | } |
1534 | | } |