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