1 | //! Contains serializer for content of an XML element
|
2 |
|
3 | use crate::errors::serialize::DeError;
|
4 | use crate::se::element::{ElementSerializer, Struct, Tuple};
|
5 | use crate::se::simple_type::{QuoteTarget, SimpleTypeSerializer};
|
6 | use crate::se::{Indent, QuoteLevel, 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 | fn $method(self, value: $ty) -> Result<Self::Ok, Self::Error> {
|
17 | self.into_simple_type_serializer().$method(value)?;
|
18 | Ok(())
|
19 | }
|
20 | };
|
21 | }
|
22 |
|
23 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
24 |
|
25 | /// A serializer used to serialize content of the element. It does not write
|
26 | /// surrounding tags.
|
27 | ///
|
28 | /// This serializer does the following:
|
29 | /// - primitives (booleans, numbers and strings) serialized as naked strings
|
30 | /// - `None` does not write anything
|
31 | /// - sequences serialized without delimiters. `[1, 2, 3]` would be serialized as `123`
|
32 | /// - units (`()`) and unit structs are not supported
|
33 | /// - structs and maps are not supported
|
34 | /// - unit variants serialized as self-closed `<${variant}/>`
|
35 | /// - tuple variants serialized as sequences where each is wrapped in
|
36 | /// `<${variant}>...</${variant}>`
|
37 | /// - struct variants serialized wrapped `<${variant}>...</${variant}>`
|
38 | ///
|
39 | /// The difference between this serializer and [`SimpleTypeSerializer`] is in how
|
40 | /// sequences and maps are serialized. Unlike `SimpleTypeSerializer` it supports
|
41 | /// any types in sequences and serializes them as list of elements, but that has
|
42 | /// drawbacks. Sequence of primitives would be serialized without delimiters and
|
43 | /// it will be impossible to distinguish between them. Even worse, when serializing
|
44 | /// with indent, sequence of strings become one big string with additional content
|
45 | /// and it would be impossible to distinguish between content of the original
|
46 | /// strings and inserted indent characters.
|
47 | pub struct ContentSerializer<'w, 'i, W: Write> {
|
48 | pub writer: &'w mut W,
|
49 | /// Defines which XML characters need to be escaped in text content
|
50 | pub level: QuoteLevel,
|
51 | /// Current indentation level. Note, that `Indent::None` means that there is
|
52 | /// no indentation at all, but `write_indent == false` means only, that indent
|
53 | /// writing is disabled in this instantiation of `ContentSerializer`, but
|
54 | /// child serializers should have access to the actual state of indentation.
|
55 | pub(super) indent: Indent<'i>,
|
56 | /// If `true`, then current indent will be written before writing the content,
|
57 | /// but only if content is not empty.
|
58 | pub write_indent: bool,
|
59 | // If `true`, then empty elements will be serialized as `<element></element>`
|
60 | // instead of `<element/>`.
|
61 | pub expand_empty_elements: bool,
|
62 | //TODO: add settings to disallow consequent serialization of primitives
|
63 | }
|
64 |
|
65 | impl<'w, 'i, W: Write> ContentSerializer<'w, 'i, W> {
|
66 | /// Turns this serializer into serializer of a text content
|
67 | #[inline ]
|
68 | pub fn into_simple_type_serializer(self) -> SimpleTypeSerializer<'i, &'w mut W> {
|
69 | //TODO: Customization point: choose between CDATA and Text representation
|
70 | SimpleTypeSerializer {
|
71 | writer: self.writer,
|
72 | target: QuoteTarget::Text,
|
73 | level: self.level,
|
74 | indent: if self.write_indent {
|
75 | self.indent
|
76 | } else {
|
77 | Indent::None
|
78 | },
|
79 | }
|
80 | }
|
81 |
|
82 | /// Creates new serializer that shares state with this serializer and
|
83 | /// writes to the same underlying writer
|
84 | #[inline ]
|
85 | pub fn new_seq_element_serializer(&mut self) -> ContentSerializer<W> {
|
86 | ContentSerializer {
|
87 | writer: self.writer,
|
88 | level: self.level,
|
89 | indent: self.indent.borrow(),
|
90 | write_indent: self.write_indent,
|
91 | expand_empty_elements: self.expand_empty_elements,
|
92 | }
|
93 | }
|
94 |
|
95 | /// Writes `name` as self-closed tag
|
96 | #[inline ]
|
97 | pub(super) fn write_empty(mut self, name: XmlName) -> Result<(), DeError> {
|
98 | self.write_indent()?;
|
99 | if self.expand_empty_elements {
|
100 | self.writer.write_char('<' )?;
|
101 | self.writer.write_str(name.0)?;
|
102 | self.writer.write_str("></" )?;
|
103 | self.writer.write_str(name.0)?;
|
104 | self.writer.write_char('>' )?;
|
105 | } else {
|
106 | self.writer.write_str("<" )?;
|
107 | self.writer.write_str(name.0)?;
|
108 | self.writer.write_str("/>" )?;
|
109 | }
|
110 | Ok(())
|
111 | }
|
112 |
|
113 | /// Writes simple type content between `name` tags
|
114 | pub(super) fn write_wrapped<S>(mut self, name: XmlName, serialize: S) -> Result<(), DeError>
|
115 | where
|
116 | S: for<'a> FnOnce(SimpleTypeSerializer<'i, &'a mut W>) -> Result<&'a mut W, DeError>,
|
117 | {
|
118 | self.write_indent()?;
|
119 | self.writer.write_char('<' )?;
|
120 | self.writer.write_str(name.0)?;
|
121 | self.writer.write_char('>' )?;
|
122 |
|
123 | let writer = serialize(self.into_simple_type_serializer())?;
|
124 |
|
125 | writer.write_str("</" )?;
|
126 | writer.write_str(name.0)?;
|
127 | writer.write_char('>' )?;
|
128 | Ok(())
|
129 | }
|
130 |
|
131 | pub(super) fn write_indent(&mut self) -> Result<(), DeError> {
|
132 | if self.write_indent {
|
133 | self.indent.write_indent(&mut self.writer)?;
|
134 | self.write_indent = false;
|
135 | }
|
136 | Ok(())
|
137 | }
|
138 | }
|
139 |
|
140 | impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> {
|
141 | type Ok = ();
|
142 | type Error = DeError;
|
143 |
|
144 | type SerializeSeq = Self;
|
145 | type SerializeTuple = Self;
|
146 | type SerializeTupleStruct = Self;
|
147 | type SerializeTupleVariant = Tuple<'w, 'i, W>;
|
148 | type SerializeMap = Impossible<Self::Ok, Self::Error>;
|
149 | type SerializeStruct = Impossible<Self::Ok, Self::Error>;
|
150 | type SerializeStructVariant = Struct<'w, 'i, W>;
|
151 |
|
152 | write_primitive!(serialize_bool(bool));
|
153 |
|
154 | write_primitive!(serialize_i8(i8));
|
155 | write_primitive!(serialize_i16(i16));
|
156 | write_primitive!(serialize_i32(i32));
|
157 | write_primitive!(serialize_i64(i64));
|
158 |
|
159 | write_primitive!(serialize_u8(u8));
|
160 | write_primitive!(serialize_u16(u16));
|
161 | write_primitive!(serialize_u32(u32));
|
162 | write_primitive!(serialize_u64(u64));
|
163 |
|
164 | serde_if_integer128! {
|
165 | write_primitive!(serialize_i128(i128));
|
166 | write_primitive!(serialize_u128(u128));
|
167 | }
|
168 |
|
169 | write_primitive!(serialize_f32(f32));
|
170 | write_primitive!(serialize_f64(f64));
|
171 |
|
172 | write_primitive!(serialize_char(char));
|
173 | write_primitive!(serialize_bytes(&[u8]));
|
174 |
|
175 | #[inline ]
|
176 | fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {
|
177 | if !value.is_empty() {
|
178 | self.into_simple_type_serializer().serialize_str(value)?;
|
179 | }
|
180 | Ok(())
|
181 | }
|
182 |
|
183 | /// Does not write anything
|
184 | #[inline ]
|
185 | fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
186 | Ok(())
|
187 | }
|
188 |
|
189 | fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, Self::Error> {
|
190 | value.serialize(self)
|
191 | }
|
192 |
|
193 | /// Does not write anything
|
194 | #[inline ]
|
195 | fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
196 | Ok(())
|
197 | }
|
198 |
|
199 | /// Does not write anything
|
200 | #[inline ]
|
201 | fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
|
202 | Ok(())
|
203 | }
|
204 |
|
205 | /// Checks `variant` for XML name validity and writes `<${variant}/>`
|
206 | fn serialize_unit_variant(
|
207 | self,
|
208 | _name: &'static str,
|
209 | _variant_index: u32,
|
210 | variant: &'static str,
|
211 | ) -> Result<Self::Ok, Self::Error> {
|
212 | let name = XmlName::try_from(variant)?;
|
213 | self.write_empty(name)
|
214 | }
|
215 |
|
216 | fn serialize_newtype_struct<T: ?Sized + Serialize>(
|
217 | self,
|
218 | _name: &'static str,
|
219 | value: &T,
|
220 | ) -> Result<Self::Ok, Self::Error> {
|
221 | value.serialize(self)
|
222 | }
|
223 |
|
224 | /// Checks `variant` for XML name validity and writes `value` as new element
|
225 | /// with name `variant`.
|
226 | fn serialize_newtype_variant<T: ?Sized + Serialize>(
|
227 | self,
|
228 | _name: &'static str,
|
229 | _variant_index: u32,
|
230 | variant: &'static str,
|
231 | value: &T,
|
232 | ) -> Result<Self::Ok, Self::Error> {
|
233 | value.serialize(ElementSerializer {
|
234 | key: XmlName::try_from(variant)?,
|
235 | ser: self,
|
236 | })
|
237 | }
|
238 |
|
239 | #[inline ]
|
240 | fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
241 | Ok(self)
|
242 | }
|
243 |
|
244 | #[inline ]
|
245 | fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
246 | self.serialize_seq(Some(len))
|
247 | }
|
248 |
|
249 | #[inline ]
|
250 | fn serialize_tuple_struct(
|
251 | self,
|
252 | _name: &'static str,
|
253 | len: usize,
|
254 | ) -> Result<Self::SerializeTupleStruct, Self::Error> {
|
255 | self.serialize_tuple(len)
|
256 | }
|
257 |
|
258 | #[inline ]
|
259 | fn serialize_tuple_variant(
|
260 | self,
|
261 | name: &'static str,
|
262 | _variant_index: u32,
|
263 | variant: &'static str,
|
264 | len: usize,
|
265 | ) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
266 | let ser = ElementSerializer {
|
267 | key: XmlName::try_from(variant)?,
|
268 | ser: self,
|
269 | };
|
270 | // `ElementSerializer::serialize_tuple_variant` is the same as
|
271 | // `ElementSerializer::serialize_tuple_struct`, except that it replaces `.key`
|
272 | // to `variant` which is not required here
|
273 | ser.serialize_tuple_struct(name, len).map(Tuple::Element)
|
274 | }
|
275 |
|
276 | fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
277 | Err(DeError::Unsupported(
|
278 | format!("serialization of map types is not supported in `$value` field" ).into(),
|
279 | ))
|
280 | }
|
281 |
|
282 | #[inline ]
|
283 | fn serialize_struct(
|
284 | self,
|
285 | name: &'static str,
|
286 | _len: usize,
|
287 | ) -> Result<Self::SerializeStruct, Self::Error> {
|
288 | Err(DeError::Unsupported(
|
289 | format!("serialization of struct ` {name}` is not supported in `$value` field" ).into(),
|
290 | ))
|
291 | }
|
292 |
|
293 | #[inline ]
|
294 | fn serialize_struct_variant(
|
295 | self,
|
296 | name: &'static str,
|
297 | _variant_index: u32,
|
298 | variant: &'static str,
|
299 | len: usize,
|
300 | ) -> Result<Self::SerializeStructVariant, Self::Error> {
|
301 | let ser = ElementSerializer {
|
302 | key: XmlName::try_from(variant)?,
|
303 | ser: self,
|
304 | };
|
305 | // `ElementSerializer::serialize_struct_variant` is the same as
|
306 | // `ElementSerializer::serialize_struct`, except that it replaces `.key`
|
307 | // to `variant` which is not required here
|
308 | ser.serialize_struct(name, len)
|
309 | }
|
310 | }
|
311 |
|
312 | impl<'w, 'i, W: Write> SerializeSeq for ContentSerializer<'w, 'i, W> {
|
313 | type Ok = ();
|
314 | type Error = DeError;
|
315 |
|
316 | fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
317 | where
|
318 | T: ?Sized + Serialize,
|
319 | {
|
320 | value.serialize(self.new_seq_element_serializer())?;
|
321 | // Write indent for next element
|
322 | self.write_indent = true;
|
323 | Ok(())
|
324 | }
|
325 |
|
326 | #[inline ]
|
327 | fn end(self) -> Result<Self::Ok, Self::Error> {
|
328 | Ok(())
|
329 | }
|
330 | }
|
331 |
|
332 | impl<'w, 'i, W: Write> SerializeTuple for ContentSerializer<'w, 'i, W> {
|
333 | type Ok = ();
|
334 | type Error = DeError;
|
335 |
|
336 | #[inline ]
|
337 | fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
338 | where
|
339 | T: ?Sized + Serialize,
|
340 | {
|
341 | <Self as SerializeSeq>::serialize_element(self, value)
|
342 | }
|
343 |
|
344 | #[inline ]
|
345 | fn end(self) -> Result<Self::Ok, Self::Error> {
|
346 | <Self as SerializeSeq>::end(self)
|
347 | }
|
348 | }
|
349 |
|
350 | impl<'w, 'i, W: Write> SerializeTupleStruct for ContentSerializer<'w, 'i, W> {
|
351 | type Ok = ();
|
352 | type Error = DeError;
|
353 |
|
354 | #[inline ]
|
355 | fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
356 | where
|
357 | T: ?Sized + Serialize,
|
358 | {
|
359 | <Self as SerializeSeq>::serialize_element(self, value)
|
360 | }
|
361 |
|
362 | #[inline ]
|
363 | fn end(self) -> Result<Self::Ok, Self::Error> {
|
364 | <Self as SerializeSeq>::end(self)
|
365 | }
|
366 | }
|
367 |
|
368 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
369 |
|
370 | /// Make tests public to reuse types in `elements::tests` module
|
371 | #[cfg (test)]
|
372 | pub(super) mod tests {
|
373 | use super::*;
|
374 | use crate::utils::Bytes;
|
375 | use serde::Serialize;
|
376 | use std::collections::BTreeMap;
|
377 |
|
378 | #[derive (Debug, Serialize, PartialEq)]
|
379 | pub struct Unit;
|
380 |
|
381 | #[derive (Debug, Serialize, PartialEq)]
|
382 | #[serde(rename = "< \"&'>" )]
|
383 | pub struct UnitEscaped;
|
384 |
|
385 | #[derive (Debug, Serialize, PartialEq)]
|
386 | pub struct Newtype(pub usize);
|
387 |
|
388 | #[derive (Debug, Serialize, PartialEq)]
|
389 | pub struct Tuple(pub &'static str, pub usize);
|
390 |
|
391 | #[derive (Debug, Serialize, PartialEq)]
|
392 | pub struct Struct {
|
393 | pub key: &'static str,
|
394 | pub val: (usize, usize),
|
395 | }
|
396 |
|
397 | #[derive (Debug, Serialize, PartialEq)]
|
398 | pub struct Text<T> {
|
399 | pub before: &'static str,
|
400 | #[serde(rename = "$text" )]
|
401 | pub content: T,
|
402 | pub after: &'static str,
|
403 | }
|
404 |
|
405 | #[derive (Debug, Serialize, PartialEq)]
|
406 | pub struct Value<T> {
|
407 | pub before: &'static str,
|
408 | #[serde(rename = "$value" )]
|
409 | pub content: T,
|
410 | pub after: &'static str,
|
411 | }
|
412 |
|
413 | /// Attributes identified by starting with `@` character
|
414 | #[derive (Debug, Serialize, PartialEq)]
|
415 | pub struct Attributes {
|
416 | #[serde(rename = "@key" )]
|
417 | pub key: &'static str,
|
418 | #[serde(rename = "@val" )]
|
419 | pub val: (usize, usize),
|
420 | }
|
421 | #[derive (Debug, Serialize, PartialEq)]
|
422 | pub struct AttributesBefore {
|
423 | #[serde(rename = "@key" )]
|
424 | pub key: &'static str,
|
425 | pub val: usize,
|
426 | }
|
427 | #[derive (Debug, Serialize, PartialEq)]
|
428 | pub struct AttributesAfter {
|
429 | pub key: &'static str,
|
430 | #[serde(rename = "@val" )]
|
431 | pub val: usize,
|
432 | }
|
433 |
|
434 | #[derive (Debug, Serialize, PartialEq)]
|
435 | pub enum Enum {
|
436 | Unit,
|
437 | /// Variant name becomes a tag name, but the name of variant is invalid
|
438 | /// XML name. Serialization of this element should be forbidden
|
439 | #[serde(rename = "< \"&'>" )]
|
440 | UnitEscaped,
|
441 | Newtype(usize),
|
442 | Tuple(&'static str, usize),
|
443 | Struct {
|
444 | key: &'static str,
|
445 | /// Should be serialized as elements
|
446 | val: (usize, usize),
|
447 | },
|
448 | Attributes {
|
449 | #[serde(rename = "@key" )]
|
450 | key: &'static str,
|
451 | #[serde(rename = "@val" )]
|
452 | val: (usize, usize),
|
453 | },
|
454 | AttributesBefore {
|
455 | #[serde(rename = "@key" )]
|
456 | key: &'static str,
|
457 | val: usize,
|
458 | },
|
459 | AttributesAfter {
|
460 | key: &'static str,
|
461 | #[serde(rename = "@val" )]
|
462 | val: usize,
|
463 | },
|
464 | }
|
465 |
|
466 | #[derive (Debug, Serialize, PartialEq)]
|
467 | pub enum SpecialEnum<T> {
|
468 | Text {
|
469 | before: &'static str,
|
470 | #[serde(rename = "$text" )]
|
471 | content: T,
|
472 | after: &'static str,
|
473 | },
|
474 | Value {
|
475 | before: &'static str,
|
476 | #[serde(rename = "$value" )]
|
477 | content: T,
|
478 | after: &'static str,
|
479 | },
|
480 | }
|
481 |
|
482 | mod without_indent {
|
483 | use super::Struct;
|
484 | use super::*;
|
485 | use pretty_assertions::assert_eq;
|
486 |
|
487 | /// Checks that given `$data` successfully serialized as `$expected`
|
488 | macro_rules! serialize_as {
|
489 | ($name:ident: $data:expr => $expected:literal) => {
|
490 | #[test]
|
491 | fn $name() {
|
492 | let mut buffer = String::new();
|
493 | let ser = ContentSerializer {
|
494 | writer: &mut buffer,
|
495 | level: QuoteLevel::Full,
|
496 | indent: Indent::None,
|
497 | write_indent: false,
|
498 | expand_empty_elements: false,
|
499 | };
|
500 |
|
501 | $data.serialize(ser).unwrap();
|
502 | assert_eq!(buffer, $expected);
|
503 | }
|
504 | };
|
505 | }
|
506 |
|
507 | /// Checks that attempt to serialize given `$data` results to a
|
508 | /// serialization error `$kind` with `$reason`
|
509 | macro_rules! err {
|
510 | ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
|
511 | #[test]
|
512 | fn $name() {
|
513 | let mut buffer = String::new();
|
514 | let ser = ContentSerializer {
|
515 | writer: &mut buffer,
|
516 | level: QuoteLevel::Full,
|
517 | indent: Indent::None,
|
518 | write_indent: false,
|
519 | expand_empty_elements: false,
|
520 | };
|
521 |
|
522 | match $data.serialize(ser).unwrap_err() {
|
523 | DeError::$kind(e) => assert_eq!(e, $reason),
|
524 | e => panic!(
|
525 | "Expected `{}({})`, found `{:?}`" ,
|
526 | stringify!($kind),
|
527 | $reason,
|
528 | e
|
529 | ),
|
530 | }
|
531 | // We could write something before fail
|
532 | // assert_eq!(buffer, "");
|
533 | }
|
534 | };
|
535 | }
|
536 |
|
537 | // Primitives is serialized in the same way as for SimpleTypeSerializer
|
538 | serialize_as!(false_: false => "false" );
|
539 | serialize_as!(true_: true => "true" );
|
540 |
|
541 | serialize_as!(i8_: -42i8 => "-42" );
|
542 | serialize_as!(i16_: -4200i16 => "-4200" );
|
543 | serialize_as!(i32_: -42000000i32 => "-42000000" );
|
544 | serialize_as!(i64_: -42000000000000i64 => "-42000000000000" );
|
545 | serialize_as!(isize_: -42000000000000isize => "-42000000000000" );
|
546 |
|
547 | serialize_as!(u8_: 42u8 => "42" );
|
548 | serialize_as!(u16_: 4200u16 => "4200" );
|
549 | serialize_as!(u32_: 42000000u32 => "42000000" );
|
550 | serialize_as!(u64_: 42000000000000u64 => "42000000000000" );
|
551 | serialize_as!(usize_: 42000000000000usize => "42000000000000" );
|
552 |
|
553 | serde_if_integer128! {
|
554 | serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
555 | serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
556 | }
|
557 |
|
558 | serialize_as!(f32_: 4.2f32 => "4.2" );
|
559 | serialize_as!(f64_: 4.2f64 => "4.2" );
|
560 |
|
561 | serialize_as!(char_non_escaped: 'h' => "h" );
|
562 | serialize_as!(char_lt: '<' => "<" );
|
563 | serialize_as!(char_gt: '>' => ">" );
|
564 | serialize_as!(char_amp: '&' => "&" );
|
565 | serialize_as!(char_apos: ' \'' => "'" );
|
566 | serialize_as!(char_quot: '"' => """ );
|
567 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
568 | // pretty-print does not change the content
|
569 | serialize_as!(char_space: ' ' => " " );
|
570 |
|
571 | serialize_as!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
572 | serialize_as!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
573 |
|
574 | err!(bytes: Bytes(b"< \"escaped & bytes'>" ) => Unsupported("`serialize_bytes` not supported yet" ));
|
575 |
|
576 | serialize_as!(option_none: Option::<Enum>::None => "" );
|
577 | serialize_as!(option_some: Some("non-escaped string" ) => "non-escaped string" );
|
578 | serialize_as!(option_some_empty_str: Some("" ) => "" );
|
579 |
|
580 | serialize_as!(unit: () => "" );
|
581 | serialize_as!(unit_struct: Unit => "" );
|
582 | serialize_as!(unit_struct_escaped: UnitEscaped => "" );
|
583 |
|
584 | // Unlike SimpleTypeSerializer, enumeration values serialized as tags
|
585 | serialize_as!(enum_unit: Enum::Unit => "<Unit/>" );
|
586 | err!(enum_unit_escaped: Enum::UnitEscaped
|
587 | => Unsupported("character `<` is not allowed at the start of an XML name `< \"&'>`" ));
|
588 |
|
589 | // Newtypes recursively applies ContentSerializer
|
590 | serialize_as!(newtype: Newtype(42) => "42" );
|
591 | serialize_as!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>" );
|
592 |
|
593 | // Note that sequences of primitives serialized without delimiters!
|
594 | serialize_as!(seq: vec![1, 2, 3] => "123" );
|
595 | serialize_as!(seq_empty: Vec::<usize>::new() => "" );
|
596 | serialize_as!(tuple: ("< \"&'>" , "with \t\r\n spaces" , 3usize)
|
597 | => "<"&'> \
|
598 | with \t\r\n spaces \
|
599 | 3" );
|
600 | serialize_as!(tuple_struct: Tuple("first" , 42)
|
601 | => "first \
|
602 | 42" );
|
603 | serialize_as!(enum_tuple: Enum::Tuple("first" , 42)
|
604 | => "<Tuple>first</Tuple> \
|
605 | <Tuple>42</Tuple>" );
|
606 |
|
607 | // Structured types cannot be serialized without surrounding tag, which
|
608 | // only `enum` can provide
|
609 | err!(map: BTreeMap::from([("_1" , 2), ("_3" , 4)])
|
610 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
611 | err!(struct_: Struct { key: "answer" , val: (42, 42) }
|
612 | => Unsupported("serialization of struct `Struct` is not supported in `$value` field" ));
|
613 | serialize_as!(enum_struct: Enum::Struct { key: "answer" , val: (42, 42) }
|
614 | => "<Struct> \
|
615 | <key>answer</key> \
|
616 | <val>42</val> \
|
617 | <val>42</val> \
|
618 | </Struct>" );
|
619 |
|
620 | /// Special field name `$text` should be serialized as a text content
|
621 | mod text {
|
622 | use super::*;
|
623 | use pretty_assertions::assert_eq;
|
624 |
|
625 | err!(map: BTreeMap::from([("$text" , 2), ("_3" , 4)])
|
626 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
627 | err!(struct_:
|
628 | Text {
|
629 | before: "answer" ,
|
630 | content: (42, 42),
|
631 | after: "answer" ,
|
632 | }
|
633 | => Unsupported("serialization of struct `Text` is not supported in `$value` field" ));
|
634 | serialize_as!(enum_struct:
|
635 | SpecialEnum::Text {
|
636 | before: "answer" ,
|
637 | content: (42, 42),
|
638 | after: "answer" ,
|
639 | }
|
640 | => "<Text> \
|
641 | <before>answer</before> \
|
642 | 42 42 \
|
643 | <after>answer</after> \
|
644 | </Text>" );
|
645 | }
|
646 |
|
647 | mod attributes {
|
648 | use super::*;
|
649 | use pretty_assertions::assert_eq;
|
650 |
|
651 | err!(map_attr: BTreeMap::from([("@key1" , 1), ("@key2" , 2)])
|
652 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
653 | err!(map_mixed: BTreeMap::from([("@key1" , 1), ("key2" , 2)])
|
654 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
655 |
|
656 | err!(struct_: Attributes { key: "answer" , val: (42, 42) }
|
657 | => Unsupported("serialization of struct `Attributes` is not supported in `$value` field" ));
|
658 | err!(struct_before: AttributesBefore { key: "answer" , val: 42 }
|
659 | => Unsupported("serialization of struct `AttributesBefore` is not supported in `$value` field" ));
|
660 | err!(struct_after: AttributesAfter { key: "answer" , val: 42 }
|
661 | => Unsupported("serialization of struct `AttributesAfter` is not supported in `$value` field" ));
|
662 |
|
663 | serialize_as!(enum_: Enum::Attributes { key: "answer" , val: (42, 42) }
|
664 | => r#"<Attributes key="answer" val="42 42"/>"# );
|
665 | serialize_as!(enum_before: Enum::AttributesBefore { key: "answer" , val: 42 }
|
666 | => r#"<AttributesBefore key="answer"><val>42</val></AttributesBefore>"# );
|
667 | serialize_as!(enum_after: Enum::AttributesAfter { key: "answer" , val: 42 }
|
668 | => r#"<AttributesAfter val="42"><key>answer</key></AttributesAfter>"# );
|
669 | }
|
670 | }
|
671 |
|
672 | mod with_indent {
|
673 | use super::Struct;
|
674 | use super::*;
|
675 | use crate::writer::Indentation;
|
676 | use pretty_assertions::assert_eq;
|
677 |
|
678 | /// Checks that given `$data` successfully serialized as `$expected`
|
679 | macro_rules! serialize_as {
|
680 | ($name:ident: $data:expr => $expected:literal) => {
|
681 | #[test]
|
682 | fn $name() {
|
683 | let mut buffer = String::new();
|
684 | let ser = ContentSerializer {
|
685 | writer: &mut buffer,
|
686 | level: QuoteLevel::Full,
|
687 | indent: Indent::Owned(Indentation::new(b' ' , 2)),
|
688 | write_indent: false,
|
689 | expand_empty_elements: false,
|
690 | };
|
691 |
|
692 | $data.serialize(ser).unwrap();
|
693 | assert_eq!(buffer, $expected);
|
694 | }
|
695 | };
|
696 | }
|
697 |
|
698 | /// Checks that attempt to serialize given `$data` results to a
|
699 | /// serialization error `$kind` with `$reason`
|
700 | macro_rules! err {
|
701 | ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
|
702 | #[test]
|
703 | fn $name() {
|
704 | let mut buffer = String::new();
|
705 | let ser = ContentSerializer {
|
706 | writer: &mut buffer,
|
707 | level: QuoteLevel::Full,
|
708 | indent: Indent::Owned(Indentation::new(b' ' , 2)),
|
709 | write_indent: false,
|
710 | expand_empty_elements: false,
|
711 | };
|
712 |
|
713 | match $data.serialize(ser).unwrap_err() {
|
714 | DeError::$kind(e) => assert_eq!(e, $reason),
|
715 | e => panic!(
|
716 | "Expected `{}({})`, found `{:?}`" ,
|
717 | stringify!($kind),
|
718 | $reason,
|
719 | e
|
720 | ),
|
721 | }
|
722 | // We can write something before fail
|
723 | // assert_eq!(buffer, "");
|
724 | }
|
725 | };
|
726 | }
|
727 |
|
728 | serialize_as!(false_: false => "false" );
|
729 | serialize_as!(true_: true => "true" );
|
730 |
|
731 | serialize_as!(i8_: -42i8 => "-42" );
|
732 | serialize_as!(i16_: -4200i16 => "-4200" );
|
733 | serialize_as!(i32_: -42000000i32 => "-42000000" );
|
734 | serialize_as!(i64_: -42000000000000i64 => "-42000000000000" );
|
735 | serialize_as!(isize_: -42000000000000isize => "-42000000000000" );
|
736 |
|
737 | serialize_as!(u8_: 42u8 => "42" );
|
738 | serialize_as!(u16_: 4200u16 => "4200" );
|
739 | serialize_as!(u32_: 42000000u32 => "42000000" );
|
740 | serialize_as!(u64_: 42000000000000u64 => "42000000000000" );
|
741 | serialize_as!(usize_: 42000000000000usize => "42000000000000" );
|
742 |
|
743 | serde_if_integer128! {
|
744 | serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
745 | serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
746 | }
|
747 |
|
748 | serialize_as!(f32_: 4.2f32 => "4.2" );
|
749 | serialize_as!(f64_: 4.2f64 => "4.2" );
|
750 |
|
751 | serialize_as!(char_non_escaped: 'h' => "h" );
|
752 | serialize_as!(char_lt: '<' => "<" );
|
753 | serialize_as!(char_gt: '>' => ">" );
|
754 | serialize_as!(char_amp: '&' => "&" );
|
755 | serialize_as!(char_apos: ' \'' => "'" );
|
756 | serialize_as!(char_quot: '"' => """ );
|
757 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
758 | // pretty-print does not change the content
|
759 | serialize_as!(char_space: ' ' => " " );
|
760 |
|
761 | serialize_as!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
762 | serialize_as!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
763 |
|
764 | err!(bytes: Bytes(b"< \"escaped & bytes'>" ) => Unsupported("`serialize_bytes` not supported yet" ));
|
765 |
|
766 | serialize_as!(option_none: Option::<Enum>::None => "" );
|
767 | serialize_as!(option_some: Some(Enum::Unit) => "<Unit/>" );
|
768 |
|
769 | serialize_as!(unit: () => "" );
|
770 | serialize_as!(unit_struct: Unit => "" );
|
771 | serialize_as!(unit_struct_escaped: UnitEscaped => "" );
|
772 |
|
773 | // Unlike SimpleTypeSerializer, enumeration values serialized as tags
|
774 | serialize_as!(enum_unit: Enum::Unit => "<Unit/>" );
|
775 | err!(enum_unit_escaped: Enum::UnitEscaped
|
776 | => Unsupported("character `<` is not allowed at the start of an XML name `< \"&'>`" ));
|
777 |
|
778 | // Newtypes recursively applies ContentSerializer
|
779 | serialize_as!(newtype: Newtype(42) => "42" );
|
780 | serialize_as!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>" );
|
781 |
|
782 | // Note that sequences of primitives serialized without delimiters other that indent!
|
783 | serialize_as!(seq: vec![1, 2, 3]
|
784 | => "1 \n\
|
785 | 2 \n\
|
786 | 3" );
|
787 | serialize_as!(seq_empty: Vec::<usize>::new() => "" );
|
788 | serialize_as!(tuple: ("< \"&'>" , "with \t\r\n spaces" , 3usize)
|
789 | => "<"&'> \n\
|
790 | with \t\r\n spaces \n\
|
791 | 3" );
|
792 | serialize_as!(tuple_struct: Tuple("first" , 42)
|
793 | => "first \n\
|
794 | 42" );
|
795 | serialize_as!(enum_tuple: Enum::Tuple("first" , 42)
|
796 | => "<Tuple>first</Tuple> \n\
|
797 | <Tuple>42</Tuple>" );
|
798 |
|
799 | // Structured types cannot be serialized without surrounding tag, which
|
800 | // only `enum` can provide
|
801 | err!(map: BTreeMap::from([("_1" , 2), ("_3" , 4)])
|
802 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
803 | err!(struct_: Struct { key: "answer" , val: (42, 42) }
|
804 | => Unsupported("serialization of struct `Struct` is not supported in `$value` field" ));
|
805 | serialize_as!(enum_struct: Enum::Struct { key: "answer" , val: (42, 42) }
|
806 | => "<Struct> \n \
|
807 | <key>answer</key> \n \
|
808 | <val>42</val> \n \
|
809 | <val>42</val> \n\
|
810 | </Struct>" );
|
811 |
|
812 | /// Special field name `$text` should be serialized as text content
|
813 | mod text {
|
814 | use super::*;
|
815 | use pretty_assertions::assert_eq;
|
816 |
|
817 | err!(map: BTreeMap::from([("$text" , 2), ("_3" , 4)])
|
818 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
819 | err!(struct_:
|
820 | Text {
|
821 | before: "answer" ,
|
822 | content: (42, 42),
|
823 | after: "answer" ,
|
824 | }
|
825 | => Unsupported("serialization of struct `Text` is not supported in `$value` field" ));
|
826 | serialize_as!(enum_struct:
|
827 | SpecialEnum::Text {
|
828 | before: "answer" ,
|
829 | content: (42, 42),
|
830 | after: "answer" ,
|
831 | }
|
832 | => "<Text> \n \
|
833 | <before>answer</before> \n \
|
834 | 42 42 \n \
|
835 | <after>answer</after> \n\
|
836 | </Text>" );
|
837 | }
|
838 |
|
839 | mod attributes {
|
840 | use super::*;
|
841 | use pretty_assertions::assert_eq;
|
842 |
|
843 | err!(map_attr: BTreeMap::from([("@key1" , 1), ("@key2" , 2)])
|
844 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
845 | err!(map_mixed: BTreeMap::from([("@key1" , 1), ("key2" , 2)])
|
846 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
847 |
|
848 | err!(struct_: Attributes { key: "answer" , val: (42, 42) }
|
849 | => Unsupported("serialization of struct `Attributes` is not supported in `$value` field" ));
|
850 | err!(struct_before: AttributesBefore { key: "answer" , val: 42 }
|
851 | => Unsupported("serialization of struct `AttributesBefore` is not supported in `$value` field" ));
|
852 | err!(struct_after: AttributesAfter { key: "answer" , val: 42 }
|
853 | => Unsupported("serialization of struct `AttributesAfter` is not supported in `$value` field" ));
|
854 |
|
855 | serialize_as!(enum_: Enum::Attributes { key: "answer" , val: (42, 42) }
|
856 | => r#"<Attributes key="answer" val="42 42"/>"# );
|
857 | serialize_as!(enum_before: Enum::AttributesBefore { key: "answer" , val: 42 }
|
858 | => "<AttributesBefore key= \"answer \"> \n \
|
859 | <val>42</val> \n\
|
860 | </AttributesBefore>" );
|
861 | serialize_as!(enum_after: Enum::AttributesAfter { key: "answer" , val: 42 }
|
862 | => "<AttributesAfter val= \"42 \"> \n \
|
863 | <key>answer</key> \n\
|
864 | </AttributesAfter>" );
|
865 | }
|
866 | }
|
867 | }
|
868 | |