1 | //! Contains serializer for an XML element
|
2 |
|
3 | use crate::de::{TEXT_KEY, VALUE_KEY};
|
4 | use crate::errors::serialize::DeError;
|
5 | use crate::se::content::ContentSerializer;
|
6 | use crate::se::key::QNameSerializer;
|
7 | use crate::se::simple_type::{QuoteTarget, SimpleSeq, SimpleTypeSerializer};
|
8 | use crate::se::{Indent, XmlName};
|
9 | use serde::ser::{
|
10 | Serialize, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple,
|
11 | SerializeTupleStruct, SerializeTupleVariant, Serializer,
|
12 | };
|
13 | use serde::serde_if_integer128;
|
14 | use std::fmt::Write;
|
15 |
|
16 | macro_rules! write_primitive {
|
17 | ($method:ident ( $ty:ty )) => {
|
18 | fn $method(self, value: $ty) -> Result<Self::Ok, Self::Error> {
|
19 | self.ser.write_wrapped(self.key, |ser| ser.$method(value))
|
20 | }
|
21 | };
|
22 | }
|
23 |
|
24 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
25 |
|
26 | /// A serializer used to serialize element with specified name.
|
27 | pub struct ElementSerializer<'w, 'k, W: Write> {
|
28 | pub ser: ContentSerializer<'w, 'k, W>,
|
29 | /// Tag name used to wrap serialized types except enum variants which uses the variant name
|
30 | pub(super) key: XmlName<'k>,
|
31 | }
|
32 |
|
33 | impl<'w, 'k, W: Write> Serializer for ElementSerializer<'w, 'k, W> {
|
34 | type Ok = ();
|
35 | type Error = DeError;
|
36 |
|
37 | type SerializeSeq = Self;
|
38 | type SerializeTuple = Self;
|
39 | type SerializeTupleStruct = Self;
|
40 | type SerializeTupleVariant = Tuple<'w, 'k, W>;
|
41 | type SerializeMap = Map<'w, 'k, W>;
|
42 | type SerializeStruct = Struct<'w, 'k, W>;
|
43 | type SerializeStructVariant = Struct<'w, 'k, W>;
|
44 |
|
45 | write_primitive!(serialize_bool(bool));
|
46 |
|
47 | write_primitive!(serialize_i8(i8));
|
48 | write_primitive!(serialize_i16(i16));
|
49 | write_primitive!(serialize_i32(i32));
|
50 | write_primitive!(serialize_i64(i64));
|
51 |
|
52 | write_primitive!(serialize_u8(u8));
|
53 | write_primitive!(serialize_u16(u16));
|
54 | write_primitive!(serialize_u32(u32));
|
55 | write_primitive!(serialize_u64(u64));
|
56 |
|
57 | serde_if_integer128! {
|
58 | write_primitive!(serialize_i128(i128));
|
59 | write_primitive!(serialize_u128(u128));
|
60 | }
|
61 |
|
62 | write_primitive!(serialize_f32(f32));
|
63 | write_primitive!(serialize_f64(f64));
|
64 |
|
65 | write_primitive!(serialize_char(char));
|
66 | write_primitive!(serialize_bytes(&[u8]));
|
67 |
|
68 | fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {
|
69 | if value.is_empty() {
|
70 | self.ser.write_empty(self.key)
|
71 | } else {
|
72 | self.ser
|
73 | .write_wrapped(self.key, |ser| ser.serialize_str(value))
|
74 | }
|
75 | }
|
76 |
|
77 | /// By serde contract we should serialize key of [`None`] values. If someone
|
78 | /// wants to skip the field entirely, he should use
|
79 | /// `#[serde(skip_serializing_if = "Option::is_none")]`.
|
80 | ///
|
81 | /// In XML when we serialize field, we write field name as:
|
82 | /// - element name, or
|
83 | /// - attribute name
|
84 | ///
|
85 | /// and field value as
|
86 | /// - content of the element, or
|
87 | /// - attribute value
|
88 | ///
|
89 | /// So serialization of `None` works the same as [serialization of `()`](#method.serialize_unit)
|
90 | fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
91 | self.serialize_unit()
|
92 | }
|
93 |
|
94 | fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, Self::Error> {
|
95 | value.serialize(self)
|
96 | }
|
97 |
|
98 | fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
99 | self.ser.write_empty(self.key)
|
100 | }
|
101 |
|
102 | fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
|
103 | self.ser.write_empty(self.key)
|
104 | }
|
105 |
|
106 | fn serialize_unit_variant(
|
107 | self,
|
108 | name: &'static str,
|
109 | _variant_index: u32,
|
110 | variant: &'static str,
|
111 | ) -> Result<Self::Ok, Self::Error> {
|
112 | if variant == TEXT_KEY {
|
113 | // We should write some text but we don't known what text to write
|
114 | Err(DeError::Unsupported(
|
115 | format!("` {}::$text` unit variant cannot be serialized" , name).into(),
|
116 | ))
|
117 | } else {
|
118 | let name = XmlName::try_from(variant)?;
|
119 | self.ser.write_empty(name)
|
120 | }
|
121 | }
|
122 |
|
123 | fn serialize_newtype_struct<T: ?Sized + Serialize>(
|
124 | self,
|
125 | _name: &'static str,
|
126 | value: &T,
|
127 | ) -> Result<Self::Ok, Self::Error> {
|
128 | value.serialize(self)
|
129 | }
|
130 |
|
131 | fn serialize_newtype_variant<T: ?Sized + Serialize>(
|
132 | mut self,
|
133 | _name: &'static str,
|
134 | _variant_index: u32,
|
135 | variant: &'static str,
|
136 | value: &T,
|
137 | ) -> Result<Self::Ok, Self::Error> {
|
138 | if variant == TEXT_KEY {
|
139 | value.serialize(self.ser.into_simple_type_serializer())?;
|
140 | Ok(())
|
141 | } else {
|
142 | self.key = XmlName::try_from(variant)?;
|
143 | value.serialize(self)
|
144 | }
|
145 | }
|
146 |
|
147 | #[inline ]
|
148 | fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
149 | Ok(self)
|
150 | }
|
151 |
|
152 | #[inline ]
|
153 | fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
154 | self.serialize_seq(Some(len))
|
155 | }
|
156 |
|
157 | #[inline ]
|
158 | fn serialize_tuple_struct(
|
159 | self,
|
160 | _name: &'static str,
|
161 | len: usize,
|
162 | ) -> Result<Self::SerializeTupleStruct, Self::Error> {
|
163 | self.serialize_tuple(len)
|
164 | }
|
165 |
|
166 | #[inline ]
|
167 | fn serialize_tuple_variant(
|
168 | mut self,
|
169 | name: &'static str,
|
170 | _variant_index: u32,
|
171 | variant: &'static str,
|
172 | len: usize,
|
173 | ) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
174 | if variant == TEXT_KEY {
|
175 | self.ser
|
176 | .into_simple_type_serializer()
|
177 | .serialize_tuple_struct(name, len)
|
178 | .map(Tuple::Text)
|
179 | } else {
|
180 | self.key = XmlName::try_from(variant)?;
|
181 | self.serialize_tuple_struct(name, len).map(Tuple::Element)
|
182 | }
|
183 | }
|
184 |
|
185 | fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
186 | Ok(Map {
|
187 | ser: self.serialize_struct("" , 0)?,
|
188 | key: None,
|
189 | })
|
190 | }
|
191 |
|
192 | #[inline ]
|
193 | fn serialize_struct(
|
194 | mut self,
|
195 | _name: &'static str,
|
196 | _len: usize,
|
197 | ) -> Result<Self::SerializeStruct, Self::Error> {
|
198 | self.ser.write_indent()?;
|
199 | self.ser.indent.increase();
|
200 |
|
201 | self.ser.writer.write_char('<' )?;
|
202 | self.ser.writer.write_str(self.key.0)?;
|
203 | Ok(Struct {
|
204 | ser: self,
|
205 | children: String::new(),
|
206 | })
|
207 | }
|
208 |
|
209 | #[inline ]
|
210 | fn serialize_struct_variant(
|
211 | mut self,
|
212 | name: &'static str,
|
213 | _variant_index: u32,
|
214 | variant: &'static str,
|
215 | len: usize,
|
216 | ) -> Result<Self::SerializeStructVariant, Self::Error> {
|
217 | if variant == TEXT_KEY {
|
218 | Err(DeError::Unsupported(
|
219 | format!("` {}::$text` struct variant cannot be serialized" , name).into(),
|
220 | ))
|
221 | } else {
|
222 | self.key = XmlName::try_from(variant)?;
|
223 | self.serialize_struct(name, len)
|
224 | }
|
225 | }
|
226 | }
|
227 |
|
228 | impl<'w, 'k, W: Write> SerializeSeq for ElementSerializer<'w, 'k, W> {
|
229 | type Ok = ();
|
230 | type Error = DeError;
|
231 |
|
232 | fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
233 | where
|
234 | T: ?Sized + Serialize,
|
235 | {
|
236 | value.serialize(serializer:ElementSerializer {
|
237 | ser: self.ser.new_seq_element_serializer(),
|
238 | key: self.key,
|
239 | })?;
|
240 | // Write indent for next element
|
241 | self.ser.write_indent = true;
|
242 | Ok(())
|
243 | }
|
244 |
|
245 | #[inline ]
|
246 | fn end(self) -> Result<Self::Ok, Self::Error> {
|
247 | Ok(())
|
248 | }
|
249 | }
|
250 |
|
251 | impl<'w, 'k, W: Write> SerializeTuple for ElementSerializer<'w, 'k, W> {
|
252 | type Ok = ();
|
253 | type Error = DeError;
|
254 |
|
255 | #[inline ]
|
256 | fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
257 | where
|
258 | T: ?Sized + Serialize,
|
259 | {
|
260 | <Self as SerializeSeq>::serialize_element(self, value)
|
261 | }
|
262 |
|
263 | #[inline ]
|
264 | fn end(self) -> Result<Self::Ok, Self::Error> {
|
265 | <Self as SerializeSeq>::end(self)
|
266 | }
|
267 | }
|
268 |
|
269 | impl<'w, 'k, W: Write> SerializeTupleStruct for ElementSerializer<'w, 'k, W> {
|
270 | type Ok = ();
|
271 | type Error = DeError;
|
272 |
|
273 | #[inline ]
|
274 | fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
275 | where
|
276 | T: ?Sized + Serialize,
|
277 | {
|
278 | <Self as SerializeSeq>::serialize_element(self, value)
|
279 | }
|
280 |
|
281 | #[inline ]
|
282 | fn end(self) -> Result<Self::Ok, Self::Error> {
|
283 | <Self as SerializeSeq>::end(self)
|
284 | }
|
285 | }
|
286 |
|
287 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
288 |
|
289 | /// A serializer for tuple variants. Tuples can be serialized in two modes:
|
290 | /// - wrapping each tuple field into a tag
|
291 | /// - without wrapping, fields are delimited by a space
|
292 | pub enum Tuple<'w, 'k, W: Write> {
|
293 | /// Serialize each tuple field as an element
|
294 | Element(ElementSerializer<'w, 'k, W>),
|
295 | /// Serialize tuple as an `xs:list`: space-delimited content of fields
|
296 | Text(SimpleSeq<'k, &'w mut W>),
|
297 | }
|
298 |
|
299 | impl<'w, 'k, W: Write> SerializeTupleVariant for Tuple<'w, 'k, W> {
|
300 | type Ok = ();
|
301 | type Error = DeError;
|
302 |
|
303 | #[inline ]
|
304 | fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
305 | where
|
306 | T: ?Sized + Serialize,
|
307 | {
|
308 | match self {
|
309 | Tuple::Element(ser: &mut ElementSerializer<'_, '_, …>) => SerializeTuple::serialize_element(self:ser, value),
|
310 | Tuple::Text(ser: &mut SimpleSeq<'_, &mut W>) => SerializeTuple::serialize_element(self:ser, value),
|
311 | }
|
312 | }
|
313 |
|
314 | #[inline ]
|
315 | fn end(self) -> Result<Self::Ok, Self::Error> {
|
316 | match self {
|
317 | Tuple::Element(ser: ElementSerializer<'_, '_, …>) => SerializeTuple::end(self:ser),
|
318 | Tuple::Text(ser: SimpleSeq<'_, &mut W>) => SerializeTuple::end(ser).map(|_| ()),
|
319 | }
|
320 | }
|
321 | }
|
322 |
|
323 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
324 |
|
325 | /// A serializer for struct variants, which serializes the struct contents inside
|
326 | /// of wrapping tags (`<${tag}>...</${tag}>`).
|
327 | ///
|
328 | /// Serialization of each field depends on it representation:
|
329 | /// - attributes written directly to the higher serializer
|
330 | /// - elements buffered into internal buffer and at the end written into higher
|
331 | /// serializer
|
332 | pub struct Struct<'w, 'k, W: Write> {
|
333 | ser: ElementSerializer<'w, 'k, W>,
|
334 | /// Buffer to store serialized elements
|
335 | // TODO: Customization point: allow direct writing of elements, but all
|
336 | // attributes should be listed first. Fail, if attribute encountered after
|
337 | // element. Use feature to configure
|
338 | children: String,
|
339 | }
|
340 |
|
341 | impl<'w, 'k, W: Write> Struct<'w, 'k, W> {
|
342 | #[inline ]
|
343 | fn write_field<T>(&mut self, key: &str, value: &T) -> Result<(), DeError>
|
344 | where
|
345 | T: ?Sized + Serialize,
|
346 | {
|
347 | //TODO: Customization point: allow user to determine if field is attribute or not
|
348 | if let Some(key) = key.strip_prefix('@' ) {
|
349 | let key = XmlName::try_from(key)?;
|
350 | self.write_attribute(key, value)
|
351 | } else {
|
352 | self.write_element(key, value)
|
353 | }
|
354 | }
|
355 |
|
356 | /// Writes `value` as an attribute
|
357 | #[inline ]
|
358 | fn write_attribute<T>(&mut self, key: XmlName, value: &T) -> Result<(), DeError>
|
359 | where
|
360 | T: ?Sized + Serialize,
|
361 | {
|
362 | //TODO: Customization point: each attribute on new line
|
363 | self.ser.ser.writer.write_char(' ' )?;
|
364 | self.ser.ser.writer.write_str(key.0)?;
|
365 | self.ser.ser.writer.write_char('=' )?;
|
366 |
|
367 | //TODO: Customization point: preferred quote style
|
368 | self.ser.ser.writer.write_char('"' )?;
|
369 | value.serialize(SimpleTypeSerializer {
|
370 | writer: &mut self.ser.ser.writer,
|
371 | target: QuoteTarget::DoubleQAttr,
|
372 | level: self.ser.ser.level,
|
373 | indent: Indent::None,
|
374 | })?;
|
375 | self.ser.ser.writer.write_char('"' )?;
|
376 |
|
377 | Ok(())
|
378 | }
|
379 |
|
380 | /// Writes `value` either as a text content, or as an element.
|
381 | ///
|
382 | /// If `key` has a magic value [`TEXT_KEY`], then `value` serialized as a
|
383 | /// [simple type].
|
384 | ///
|
385 | /// If `key` has a magic value [`VALUE_KEY`], then `value` serialized as a
|
386 | /// [content] without wrapping in tags, otherwise it is wrapped in
|
387 | /// `<${key}>...</${key}>`.
|
388 | ///
|
389 | /// [simple type]: SimpleTypeSerializer
|
390 | /// [content]: ContentSerializer
|
391 | fn write_element<T>(&mut self, key: &str, value: &T) -> Result<(), DeError>
|
392 | where
|
393 | T: ?Sized + Serialize,
|
394 | {
|
395 | let ser = ContentSerializer {
|
396 | writer: &mut self.children,
|
397 | level: self.ser.ser.level,
|
398 | indent: self.ser.ser.indent.borrow(),
|
399 | write_indent: true,
|
400 | expand_empty_elements: self.ser.ser.expand_empty_elements,
|
401 | };
|
402 |
|
403 | if key == TEXT_KEY {
|
404 | value.serialize(ser.into_simple_type_serializer())?;
|
405 | } else if key == VALUE_KEY {
|
406 | value.serialize(ser)?;
|
407 | } else {
|
408 | value.serialize(ElementSerializer {
|
409 | key: XmlName::try_from(key)?,
|
410 | ser,
|
411 | })?;
|
412 | }
|
413 | Ok(())
|
414 | }
|
415 | }
|
416 |
|
417 | impl<'w, 'k, W: Write> SerializeStruct for Struct<'w, 'k, W> {
|
418 | type Ok = ();
|
419 | type Error = DeError;
|
420 |
|
421 | fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
|
422 | where
|
423 | T: ?Sized + Serialize,
|
424 | {
|
425 | self.write_field(key, value)
|
426 | }
|
427 |
|
428 | fn end(mut self) -> Result<Self::Ok, Self::Error> {
|
429 | self.ser.ser.indent.decrease();
|
430 |
|
431 | if self.children.is_empty() {
|
432 | self.ser.ser.writer.write_str("/>" )?;
|
433 | } else {
|
434 | self.ser.ser.writer.write_char('>' )?;
|
435 | self.ser.ser.writer.write_str(&self.children)?;
|
436 |
|
437 | self.ser.ser.indent.write_indent(&mut self.ser.ser.writer)?;
|
438 |
|
439 | self.ser.ser.writer.write_str("</" )?;
|
440 | self.ser.ser.writer.write_str(self.ser.key.0)?;
|
441 | self.ser.ser.writer.write_char('>' )?;
|
442 | }
|
443 | Ok(())
|
444 | }
|
445 | }
|
446 |
|
447 | impl<'w, 'k, W: Write> SerializeStructVariant for Struct<'w, 'k, W> {
|
448 | type Ok = ();
|
449 | type Error = DeError;
|
450 |
|
451 | #[inline ]
|
452 | fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
|
453 | where
|
454 | T: ?Sized + Serialize,
|
455 | {
|
456 | <Self as SerializeStruct>::serialize_field(self, key, value)
|
457 | }
|
458 |
|
459 | #[inline ]
|
460 | fn end(self) -> Result<Self::Ok, Self::Error> {
|
461 | <Self as SerializeStruct>::end(self)
|
462 | }
|
463 | }
|
464 |
|
465 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
466 |
|
467 | pub struct Map<'w, 'k, W: Write> {
|
468 | ser: Struct<'w, 'k, W>,
|
469 | /// Key, serialized by `QNameSerializer` if consumer uses `serialize_key` +
|
470 | /// `serialize_value` calls instead of `serialize_entry`
|
471 | key: Option<String>,
|
472 | }
|
473 |
|
474 | impl<'w, 'k, W: Write> Map<'w, 'k, W> {
|
475 | fn make_key<T>(&mut self, key: &T) -> Result<String, DeError>
|
476 | where
|
477 | T: ?Sized + Serialize,
|
478 | {
|
479 | key.serialize(serializer:QNameSerializer {
|
480 | writer: String::new(),
|
481 | })
|
482 | }
|
483 | }
|
484 |
|
485 | impl<'w, 'k, W: Write> SerializeMap for Map<'w, 'k, W> {
|
486 | type Ok = ();
|
487 | type Error = DeError;
|
488 |
|
489 | fn serialize_key<T>(&mut self, key: &T) -> Result<(), Self::Error>
|
490 | where
|
491 | T: ?Sized + Serialize,
|
492 | {
|
493 | if let Some(_) = self.key.take() {
|
494 | return Err(DeError::Custom(
|
495 | "calling `serialize_key` twice without `serialize_value`" .to_string(),
|
496 | ));
|
497 | }
|
498 | self.key = Some(self.make_key(key)?);
|
499 | Ok(())
|
500 | }
|
501 |
|
502 | fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
503 | where
|
504 | T: ?Sized + Serialize,
|
505 | {
|
506 | if let Some(key) = self.key.take() {
|
507 | return self.ser.write_field(&key, value);
|
508 | }
|
509 | Err(DeError::Custom(
|
510 | "calling `serialize_value` without call of `serialize_key`" .to_string(),
|
511 | ))
|
512 | }
|
513 |
|
514 | fn serialize_entry<K, V>(&mut self, key: &K, value: &V) -> Result<(), Self::Error>
|
515 | where
|
516 | K: ?Sized + Serialize,
|
517 | V: ?Sized + Serialize,
|
518 | {
|
519 | let key = self.make_key(key)?;
|
520 | self.ser.write_field(&key, value)
|
521 | }
|
522 |
|
523 | fn end(mut self) -> Result<Self::Ok, Self::Error> {
|
524 | if let Some(key) = self.key.take() {
|
525 | return Err(DeError::Custom(format!(
|
526 | "calling `end` without call of `serialize_value` for key ` {key}`"
|
527 | )));
|
528 | }
|
529 | SerializeStruct::end(self.ser)
|
530 | }
|
531 | }
|
532 |
|
533 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
534 |
|
535 | #[cfg (test)]
|
536 | mod tests {
|
537 | use super::*;
|
538 | use crate::se::content::tests::*;
|
539 | use crate::se::{Indent, QuoteLevel};
|
540 | use crate::utils::Bytes;
|
541 | use serde::Serialize;
|
542 | use std::collections::BTreeMap;
|
543 |
|
544 | #[derive (Debug, Serialize, PartialEq)]
|
545 | struct OptionalElements {
|
546 | a: Option<&'static str>,
|
547 |
|
548 | #[serde(skip_serializing_if = "Option::is_none" )]
|
549 | b: Option<&'static str>,
|
550 | }
|
551 | #[derive (Debug, Serialize, PartialEq)]
|
552 | struct OptionalAttributes {
|
553 | #[serde(rename = "@a" )]
|
554 | a: Option<&'static str>,
|
555 |
|
556 | #[serde(rename = "@b" )]
|
557 | #[serde(skip_serializing_if = "Option::is_none" )]
|
558 | b: Option<&'static str>,
|
559 | }
|
560 |
|
561 | mod without_indent {
|
562 | use super::*;
|
563 | use crate::se::content::tests::Struct;
|
564 | use pretty_assertions::assert_eq;
|
565 |
|
566 | /// Checks that given `$data` successfully serialized as `$expected`
|
567 | macro_rules! serialize_as {
|
568 | ($name:ident: $data:expr => $expected:expr) => {
|
569 | #[test]
|
570 | fn $name() {
|
571 | let mut buffer = String::new();
|
572 | let ser = ElementSerializer {
|
573 | ser: ContentSerializer {
|
574 | writer: &mut buffer,
|
575 | level: QuoteLevel::Full,
|
576 | indent: Indent::None,
|
577 | write_indent: false,
|
578 | expand_empty_elements: false,
|
579 | },
|
580 | key: XmlName("root" ),
|
581 | };
|
582 |
|
583 | $data.serialize(ser).unwrap();
|
584 | assert_eq!(buffer, $expected);
|
585 | }
|
586 | };
|
587 | }
|
588 |
|
589 | /// Checks that attempt to serialize given `$data` results to a
|
590 | /// serialization error `$kind` with `$reason`
|
591 | macro_rules! err {
|
592 | ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
|
593 | #[test]
|
594 | fn $name() {
|
595 | let mut buffer = String::new();
|
596 | let ser = ElementSerializer {
|
597 | ser: ContentSerializer {
|
598 | writer: &mut buffer,
|
599 | level: QuoteLevel::Full,
|
600 | indent: Indent::None,
|
601 | write_indent: false,
|
602 | expand_empty_elements: false,
|
603 | },
|
604 | key: XmlName("root" ),
|
605 | };
|
606 |
|
607 | match $data.serialize(ser).unwrap_err() {
|
608 | DeError::$kind(e) => assert_eq!(e, $reason),
|
609 | e => panic!(
|
610 | "Expected `{}({})`, found `{:?}`" ,
|
611 | stringify!($kind),
|
612 | $reason,
|
613 | e
|
614 | ),
|
615 | }
|
616 | // We can write something before fail
|
617 | // assert_eq!(buffer, "");
|
618 | }
|
619 | };
|
620 | }
|
621 |
|
622 | serialize_as!(false_: false => "<root>false</root>" );
|
623 | serialize_as!(true_: true => "<root>true</root>" );
|
624 |
|
625 | serialize_as!(i8_: -42i8 => "<root>-42</root>" );
|
626 | serialize_as!(i16_: -4200i16 => "<root>-4200</root>" );
|
627 | serialize_as!(i32_: -42000000i32 => "<root>-42000000</root>" );
|
628 | serialize_as!(i64_: -42000000000000i64 => "<root>-42000000000000</root>" );
|
629 | serialize_as!(isize_: -42000000000000isize => "<root>-42000000000000</root>" );
|
630 |
|
631 | serialize_as!(u8_: 42u8 => "<root>42</root>" );
|
632 | serialize_as!(u16_: 4200u16 => "<root>4200</root>" );
|
633 | serialize_as!(u32_: 42000000u32 => "<root>42000000</root>" );
|
634 | serialize_as!(u64_: 42000000000000u64 => "<root>42000000000000</root>" );
|
635 | serialize_as!(usize_: 42000000000000usize => "<root>42000000000000</root>" );
|
636 |
|
637 | serde_if_integer128! {
|
638 | serialize_as!(i128_: -420000000000000000000000000000i128 => "<root>-420000000000000000000000000000</root>" );
|
639 | serialize_as!(u128_: 420000000000000000000000000000u128 => "<root>420000000000000000000000000000</root>" );
|
640 | }
|
641 |
|
642 | serialize_as!(f32_: 4.2f32 => "<root>4.2</root>" );
|
643 | serialize_as!(f64_: 4.2f64 => "<root>4.2</root>" );
|
644 |
|
645 | serialize_as!(char_non_escaped: 'h' => "<root>h</root>" );
|
646 | serialize_as!(char_lt: '<' => "<root><</root>" );
|
647 | serialize_as!(char_gt: '>' => "<root>></root>" );
|
648 | serialize_as!(char_amp: '&' => "<root>&</root>" );
|
649 | serialize_as!(char_apos: ' \'' => "<root>'</root>" );
|
650 | serialize_as!(char_quot: '"' => "<root>"</root>" );
|
651 |
|
652 | serialize_as!(str_non_escaped: "non-escaped string" => "<root>non-escaped string</root>" );
|
653 | serialize_as!(str_escaped: "< \"escaped & string'>" => "<root><"escaped & string'></root>" );
|
654 |
|
655 | err!(bytes: Bytes(b"< \"escaped & bytes'>" ) => Unsupported("`serialize_bytes` not supported yet" ));
|
656 |
|
657 | serialize_as!(option_none: Option::<&str>::None => "<root/>" );
|
658 | serialize_as!(option_some: Some("non-escaped string" ) => "<root>non-escaped string</root>" );
|
659 | serialize_as!(option_some_empty_str: Some("" ) => "<root/>" );
|
660 |
|
661 | serialize_as!(unit: () => "<root/>" );
|
662 | serialize_as!(unit_struct: Unit => "<root/>" );
|
663 | serialize_as!(unit_struct_escaped: UnitEscaped => "<root/>" );
|
664 |
|
665 | serialize_as!(enum_unit: Enum::Unit => "<Unit/>" );
|
666 | err!(enum_unit_escaped: Enum::UnitEscaped
|
667 | => Unsupported("character `<` is not allowed at the start of an XML name `< \"&'>`" ));
|
668 |
|
669 | serialize_as!(newtype: Newtype(42) => "<root>42</root>" );
|
670 | serialize_as!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>" );
|
671 |
|
672 | serialize_as!(seq: vec![1, 2, 3]
|
673 | => "<root>1</root> \
|
674 | <root>2</root> \
|
675 | <root>3</root>" );
|
676 | serialize_as!(seq_empty: Vec::<usize>::new() => "" );
|
677 | serialize_as!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
678 | => "<root><"&'></root> \
|
679 | <root>with \t\n\r spaces</root> \
|
680 | <root>3</root>" );
|
681 | serialize_as!(tuple_struct: Tuple("first" , 42)
|
682 | => "<root>first</root> \
|
683 | <root>42</root>" );
|
684 | serialize_as!(enum_tuple: Enum::Tuple("first" , 42)
|
685 | => "<Tuple>first</Tuple> \
|
686 | <Tuple>42</Tuple>" );
|
687 |
|
688 | serialize_as!(map: BTreeMap::from([("_1" , 2), ("_3" , 4)])
|
689 | => "<root> \
|
690 | <_1>2</_1> \
|
691 | <_3>4</_3> \
|
692 | </root>" );
|
693 | serialize_as!(struct_: Struct { key: "answer" , val: (42, 42) }
|
694 | => "<root> \
|
695 | <key>answer</key> \
|
696 | <val>42</val> \
|
697 | <val>42</val> \
|
698 | </root>" );
|
699 | serialize_as!(enum_struct: Enum::Struct { key: "answer" , val: (42, 42) }
|
700 | => "<Struct> \
|
701 | <key>answer</key> \
|
702 | <val>42</val> \
|
703 | <val>42</val> \
|
704 | </Struct>" );
|
705 |
|
706 | /// Special field name `$text` should be serialized as text content.
|
707 | /// Sequences serialized as an `xs:list` content
|
708 | mod text {
|
709 | use super::*;
|
710 |
|
711 | /// `$text` key in a map
|
712 | mod map {
|
713 | use super::*;
|
714 | use pretty_assertions::assert_eq;
|
715 |
|
716 | macro_rules! text {
|
717 | ($name:ident: $data:expr) => {
|
718 | serialize_as!($name:
|
719 | BTreeMap::from([("$text" , $data)])
|
720 | => "<root/>" );
|
721 | };
|
722 | ($name:ident: $data:expr => $expected:literal) => {
|
723 | serialize_as!($name:
|
724 | BTreeMap::from([("$text" , $data)])
|
725 | => concat!("<root>" , $expected,"</root>" ));
|
726 | };
|
727 | }
|
728 |
|
729 | text!(false_: false => "false" );
|
730 | text!(true_: true => "true" );
|
731 |
|
732 | text!(i8_: -42i8 => "-42" );
|
733 | text!(i16_: -4200i16 => "-4200" );
|
734 | text!(i32_: -42000000i32 => "-42000000" );
|
735 | text!(i64_: -42000000000000i64 => "-42000000000000" );
|
736 | text!(isize_: -42000000000000isize => "-42000000000000" );
|
737 |
|
738 | text!(u8_: 42u8 => "42" );
|
739 | text!(u16_: 4200u16 => "4200" );
|
740 | text!(u32_: 42000000u32 => "42000000" );
|
741 | text!(u64_: 42000000000000u64 => "42000000000000" );
|
742 | text!(usize_: 42000000000000usize => "42000000000000" );
|
743 |
|
744 | serde_if_integer128! {
|
745 | text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
746 | text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
747 | }
|
748 |
|
749 | text!(f32_: 4.2f32 => "4.2" );
|
750 | text!(f64_: 4.2f64 => "4.2" );
|
751 |
|
752 | text!(char_non_escaped: 'h' => "h" );
|
753 | text!(char_lt: '<' => "<" );
|
754 | text!(char_gt: '>' => ">" );
|
755 | text!(char_amp: '&' => "&" );
|
756 | text!(char_apos: ' \'' => "'" );
|
757 | text!(char_quot: '"' => """ );
|
758 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
759 | // pretty-print does not change the content
|
760 | text!(char_space: ' ' => " " );
|
761 |
|
762 | text!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
763 | text!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
764 |
|
765 | err!(bytes:
|
766 | Text {
|
767 | before: "answer" ,
|
768 | content: Bytes(b"< \"escaped & bytes'>" ),
|
769 | after: "answer" ,
|
770 | }
|
771 | => Unsupported("`serialize_bytes` not supported yet" ));
|
772 |
|
773 | text!(option_none: Option::<&str>::None);
|
774 | text!(option_some: Some("non-escaped string" ) => "non-escaped string" );
|
775 | text!(option_some_empty_str: Some("" ));
|
776 |
|
777 | text!(unit: ());
|
778 | text!(unit_struct: Unit);
|
779 | text!(unit_struct_escaped: UnitEscaped);
|
780 |
|
781 | text!(enum_unit: Enum::Unit => "Unit" );
|
782 | text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>" );
|
783 |
|
784 | text!(newtype: Newtype(42) => "42" );
|
785 | // We have no space where name of a variant can be stored
|
786 | err!(enum_newtype:
|
787 | Text {
|
788 | before: "answer" ,
|
789 | content: Enum::Newtype(42),
|
790 | after: "answer" ,
|
791 | }
|
792 | => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an attribute or text content value" ));
|
793 |
|
794 | // Sequences are serialized separated by spaces, all spaces inside are escaped
|
795 | text!(seq: vec![1, 2, 3] => "1 2 3" );
|
796 | text!(seq_empty: Vec::<usize>::new());
|
797 | text!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
798 | => "<"&'> \
|
799 | with	  spaces \
|
800 | 3" );
|
801 | text!(tuple_struct: Tuple("first" , 42) => "first 42" );
|
802 | // We have no space where name of a variant can be stored
|
803 | err!(enum_tuple:
|
804 | Text {
|
805 | before: "answer" ,
|
806 | content: Enum::Tuple("first" , 42),
|
807 | after: "answer" ,
|
808 | }
|
809 | => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an attribute or text content value" ));
|
810 |
|
811 | // Complex types cannot be serialized in `$text` field
|
812 | err!(map:
|
813 | Text {
|
814 | before: "answer" ,
|
815 | content: BTreeMap::from([("_1" , 2), ("_3" , 4)]),
|
816 | after: "answer" ,
|
817 | }
|
818 | => Unsupported("map cannot be serialized as an attribute or text content value" ));
|
819 | err!(struct_:
|
820 | Text {
|
821 | before: "answer" ,
|
822 | content: Struct { key: "answer" , val: (42, 42) },
|
823 | after: "answer" ,
|
824 | }
|
825 | => Unsupported("struct `Struct` cannot be serialized as an attribute or text content value" ));
|
826 | err!(enum_struct:
|
827 | Text {
|
828 | before: "answer" ,
|
829 | content: Enum::Struct { key: "answer" , val: (42, 42) },
|
830 | after: "answer" ,
|
831 | }
|
832 | => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an attribute or text content value" ));
|
833 | }
|
834 |
|
835 | /// `$text` field inside a struct
|
836 | mod struct_ {
|
837 | use super::*;
|
838 | use pretty_assertions::assert_eq;
|
839 |
|
840 | macro_rules! text {
|
841 | ($name:ident: $data:expr => $expected:literal) => {
|
842 | serialize_as!($name:
|
843 | Text {
|
844 | before: "answer" ,
|
845 | content: $data,
|
846 | after: "answer" ,
|
847 | }
|
848 | => concat!(
|
849 | "<root><before>answer</before>" ,
|
850 | $expected,
|
851 | "<after>answer</after></root>" ,
|
852 | ));
|
853 | };
|
854 | }
|
855 |
|
856 | text!(false_: false => "false" );
|
857 | text!(true_: true => "true" );
|
858 |
|
859 | text!(i8_: -42i8 => "-42" );
|
860 | text!(i16_: -4200i16 => "-4200" );
|
861 | text!(i32_: -42000000i32 => "-42000000" );
|
862 | text!(i64_: -42000000000000i64 => "-42000000000000" );
|
863 | text!(isize_: -42000000000000isize => "-42000000000000" );
|
864 |
|
865 | text!(u8_: 42u8 => "42" );
|
866 | text!(u16_: 4200u16 => "4200" );
|
867 | text!(u32_: 42000000u32 => "42000000" );
|
868 | text!(u64_: 42000000000000u64 => "42000000000000" );
|
869 | text!(usize_: 42000000000000usize => "42000000000000" );
|
870 |
|
871 | serde_if_integer128! {
|
872 | text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
873 | text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
874 | }
|
875 |
|
876 | text!(f32_: 4.2f32 => "4.2" );
|
877 | text!(f64_: 4.2f64 => "4.2" );
|
878 |
|
879 | text!(char_non_escaped: 'h' => "h" );
|
880 | text!(char_lt: '<' => "<" );
|
881 | text!(char_gt: '>' => ">" );
|
882 | text!(char_amp: '&' => "&" );
|
883 | text!(char_apos: ' \'' => "'" );
|
884 | text!(char_quot: '"' => """ );
|
885 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
886 | // pretty-print does not change the content
|
887 | text!(char_space: ' ' => " " );
|
888 |
|
889 | text!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
890 | text!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
891 |
|
892 | err!(bytes:
|
893 | Text {
|
894 | before: "answer" ,
|
895 | content: Bytes(b"< \"escaped & bytes'>" ),
|
896 | after: "answer" ,
|
897 | }
|
898 | => Unsupported("`serialize_bytes` not supported yet" ));
|
899 |
|
900 | text!(option_none: Option::<&str>::None => "" );
|
901 | text!(option_some: Some("non-escaped string" ) => "non-escaped string" );
|
902 | text!(option_some_empty_str: Some("" ) => "" );
|
903 |
|
904 | text!(unit: () => "" );
|
905 | text!(unit_struct: Unit => "" );
|
906 | text!(unit_struct_escaped: UnitEscaped => "" );
|
907 |
|
908 | text!(enum_unit: Enum::Unit => "Unit" );
|
909 | text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>" );
|
910 |
|
911 | text!(newtype: Newtype(42) => "42" );
|
912 | // We have no space where name of a variant can be stored
|
913 | err!(enum_newtype:
|
914 | Text {
|
915 | before: "answer" ,
|
916 | content: Enum::Newtype(42),
|
917 | after: "answer" ,
|
918 | }
|
919 | => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an attribute or text content value" ));
|
920 |
|
921 | // Sequences are serialized separated by spaces, all spaces inside are escaped
|
922 | text!(seq: vec![1, 2, 3] => "1 2 3" );
|
923 | text!(seq_empty: Vec::<usize>::new() => "" );
|
924 | text!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
925 | => "<"&'> \
|
926 | with	  spaces \
|
927 | 3" );
|
928 | text!(tuple_struct: Tuple("first" , 42) => "first 42" );
|
929 | // We have no space where name of a variant can be stored
|
930 | err!(enum_tuple:
|
931 | Text {
|
932 | before: "answer" ,
|
933 | content: Enum::Tuple("first" , 42),
|
934 | after: "answer" ,
|
935 | }
|
936 | => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an attribute or text content value" ));
|
937 |
|
938 | // Complex types cannot be serialized in `$text` field
|
939 | err!(map:
|
940 | Text {
|
941 | before: "answer" ,
|
942 | content: BTreeMap::from([("_1" , 2), ("_3" , 4)]),
|
943 | after: "answer" ,
|
944 | }
|
945 | => Unsupported("map cannot be serialized as an attribute or text content value" ));
|
946 | err!(struct_:
|
947 | Text {
|
948 | before: "answer" ,
|
949 | content: Struct { key: "answer" , val: (42, 42) },
|
950 | after: "answer" ,
|
951 | }
|
952 | => Unsupported("struct `Struct` cannot be serialized as an attribute or text content value" ));
|
953 | err!(enum_struct:
|
954 | Text {
|
955 | before: "answer" ,
|
956 | content: Enum::Struct { key: "answer" , val: (42, 42) },
|
957 | after: "answer" ,
|
958 | }
|
959 | => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an attribute or text content value" ));
|
960 | }
|
961 |
|
962 | /// `$text` field inside a struct variant of an enum
|
963 | mod enum_struct {
|
964 | use super::*;
|
965 | use pretty_assertions::assert_eq;
|
966 |
|
967 | macro_rules! text {
|
968 | ($name:ident: $data:expr => $expected:literal) => {
|
969 | serialize_as!($name:
|
970 | SpecialEnum::Text {
|
971 | before: "answer" ,
|
972 | content: $data,
|
973 | after: "answer" ,
|
974 | }
|
975 | => concat!(
|
976 | "<Text><before>answer</before>" ,
|
977 | $expected,
|
978 | "<after>answer</after></Text>" ,
|
979 | ));
|
980 | };
|
981 | }
|
982 |
|
983 | text!(false_: false => "false" );
|
984 | text!(true_: true => "true" );
|
985 |
|
986 | text!(i8_: -42i8 => "-42" );
|
987 | text!(i16_: -4200i16 => "-4200" );
|
988 | text!(i32_: -42000000i32 => "-42000000" );
|
989 | text!(i64_: -42000000000000i64 => "-42000000000000" );
|
990 | text!(isize_: -42000000000000isize => "-42000000000000" );
|
991 |
|
992 | text!(u8_: 42u8 => "42" );
|
993 | text!(u16_: 4200u16 => "4200" );
|
994 | text!(u32_: 42000000u32 => "42000000" );
|
995 | text!(u64_: 42000000000000u64 => "42000000000000" );
|
996 | text!(usize_: 42000000000000usize => "42000000000000" );
|
997 |
|
998 | serde_if_integer128! {
|
999 | text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
1000 | text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
1001 | }
|
1002 |
|
1003 | text!(f32_: 4.2f32 => "4.2" );
|
1004 | text!(f64_: 4.2f64 => "4.2" );
|
1005 |
|
1006 | text!(char_non_escaped: 'h' => "h" );
|
1007 | text!(char_lt: '<' => "<" );
|
1008 | text!(char_gt: '>' => ">" );
|
1009 | text!(char_amp: '&' => "&" );
|
1010 | text!(char_apos: ' \'' => "'" );
|
1011 | text!(char_quot: '"' => """ );
|
1012 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
1013 | // pretty-print does not change the content
|
1014 | text!(char_space: ' ' => " " );
|
1015 |
|
1016 | text!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
1017 | text!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
1018 |
|
1019 | err!(bytes:
|
1020 | SpecialEnum::Text {
|
1021 | before: "answer" ,
|
1022 | content: Bytes(b"< \"escaped & bytes'>" ),
|
1023 | after: "answer" ,
|
1024 | }
|
1025 | => Unsupported("`serialize_bytes` not supported yet" ));
|
1026 |
|
1027 | text!(option_none: Option::<&str>::None => "" );
|
1028 | text!(option_some: Some("non-escaped string" ) => "non-escaped string" );
|
1029 | text!(option_some_empty_str: Some("" ) => "" );
|
1030 |
|
1031 | text!(unit: () => "" );
|
1032 | text!(unit_struct: Unit => "" );
|
1033 | text!(unit_struct_escaped: UnitEscaped => "" );
|
1034 |
|
1035 | text!(enum_unit: Enum::Unit => "Unit" );
|
1036 | text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>" );
|
1037 |
|
1038 | text!(newtype: Newtype(42) => "42" );
|
1039 | // We have no space where name of a variant can be stored
|
1040 | err!(enum_newtype:
|
1041 | SpecialEnum::Text {
|
1042 | before: "answer" ,
|
1043 | content: Enum::Newtype(42),
|
1044 | after: "answer" ,
|
1045 | }
|
1046 | => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an attribute or text content value" ));
|
1047 |
|
1048 | // Sequences are serialized separated by spaces, all spaces inside are escaped
|
1049 | text!(seq: vec![1, 2, 3] => "1 2 3" );
|
1050 | text!(seq_empty: Vec::<usize>::new() => "" );
|
1051 | text!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
1052 | => "<"&'> \
|
1053 | with	  spaces \
|
1054 | 3" );
|
1055 | text!(tuple_struct: Tuple("first" , 42) => "first 42" );
|
1056 | // We have no space where name of a variant can be stored
|
1057 | err!(enum_tuple:
|
1058 | SpecialEnum::Text {
|
1059 | before: "answer" ,
|
1060 | content: Enum::Tuple("first" , 42),
|
1061 | after: "answer" ,
|
1062 | }
|
1063 | => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an attribute or text content value" ));
|
1064 |
|
1065 | // Complex types cannot be serialized in `$text` field
|
1066 | err!(map:
|
1067 | SpecialEnum::Text {
|
1068 | before: "answer" ,
|
1069 | content: BTreeMap::from([("_1" , 2), ("_3" , 4)]),
|
1070 | after: "answer" ,
|
1071 | }
|
1072 | => Unsupported("map cannot be serialized as an attribute or text content value" ));
|
1073 | err!(struct_:
|
1074 | SpecialEnum::Text {
|
1075 | before: "answer" ,
|
1076 | content: Struct { key: "answer" , val: (42, 42) },
|
1077 | after: "answer" ,
|
1078 | }
|
1079 | => Unsupported("struct `Struct` cannot be serialized as an attribute or text content value" ));
|
1080 | err!(enum_struct:
|
1081 | SpecialEnum::Text {
|
1082 | before: "answer" ,
|
1083 | content: Enum::Struct { key: "answer" , val: (42, 42) },
|
1084 | after: "answer" ,
|
1085 | }
|
1086 | => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an attribute or text content value" ));
|
1087 | }
|
1088 | }
|
1089 |
|
1090 | /// Special field name `$value` should be serialized using name, provided
|
1091 | /// by the type of value instead of a key. Sequences serialized as a list
|
1092 | /// of tags with that name (each element can have their own name)
|
1093 | mod value {
|
1094 | use super::*;
|
1095 |
|
1096 | /// `$value` key in a map
|
1097 | mod map {
|
1098 | use super::*;
|
1099 | use pretty_assertions::assert_eq;
|
1100 |
|
1101 | macro_rules! value {
|
1102 | ($name:ident: $data:expr) => {
|
1103 | serialize_as!($name:
|
1104 | BTreeMap::from([("$value" , $data)])
|
1105 | => "<root/>" );
|
1106 | };
|
1107 | ($name:ident: $data:expr => $expected:literal) => {
|
1108 | serialize_as!($name:
|
1109 | BTreeMap::from([("$value" , $data)])
|
1110 | => concat!("<root>" , $expected,"</root>" ));
|
1111 | };
|
1112 | }
|
1113 |
|
1114 | value!(false_: false => "false" );
|
1115 | value!(true_: true => "true" );
|
1116 |
|
1117 | value!(i8_: -42i8 => "-42" );
|
1118 | value!(i16_: -4200i16 => "-4200" );
|
1119 | value!(i32_: -42000000i32 => "-42000000" );
|
1120 | value!(i64_: -42000000000000i64 => "-42000000000000" );
|
1121 | value!(isize_: -42000000000000isize => "-42000000000000" );
|
1122 |
|
1123 | value!(u8_: 42u8 => "42" );
|
1124 | value!(u16_: 4200u16 => "4200" );
|
1125 | value!(u32_: 42000000u32 => "42000000" );
|
1126 | value!(u64_: 42000000000000u64 => "42000000000000" );
|
1127 | value!(usize_: 42000000000000usize => "42000000000000" );
|
1128 |
|
1129 | serde_if_integer128! {
|
1130 | value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
1131 | value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
1132 | }
|
1133 |
|
1134 | value!(f32_: 4.2f32 => "4.2" );
|
1135 | value!(f64_: 4.2f64 => "4.2" );
|
1136 |
|
1137 | value!(char_non_escaped: 'h' => "h" );
|
1138 | value!(char_lt: '<' => "<" );
|
1139 | value!(char_gt: '>' => ">" );
|
1140 | value!(char_amp: '&' => "&" );
|
1141 | value!(char_apos: ' \'' => "'" );
|
1142 | value!(char_quot: '"' => """ );
|
1143 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
1144 | // pretty-print does not change the content
|
1145 | value!(char_space: ' ' => " " );
|
1146 |
|
1147 | value!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
1148 | value!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
1149 |
|
1150 | err!(bytes:
|
1151 | BTreeMap::from([("$value" , Bytes(b"< \"escaped & bytes'>" ))])
|
1152 | => Unsupported("`serialize_bytes` not supported yet" ));
|
1153 |
|
1154 | value!(option_none: Option::<&str>::None);
|
1155 | value!(option_some: Some("non-escaped string" ) => "non-escaped string" );
|
1156 | value!(option_some_empty_str: Some("" ));
|
1157 |
|
1158 | value!(unit: ());
|
1159 | value!(unit_struct: Unit);
|
1160 | value!(unit_struct_escaped: UnitEscaped);
|
1161 |
|
1162 | value!(enum_unit: Enum::Unit => "<Unit/>" );
|
1163 | err!(enum_unit_escaped:
|
1164 | BTreeMap::from([("$value" , Enum::UnitEscaped)])
|
1165 | => Unsupported("character `<` is not allowed at the start of an XML name `< \"&'>`" ));
|
1166 |
|
1167 | value!(newtype: Newtype(42) => "42" );
|
1168 | value!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>" );
|
1169 |
|
1170 | // Note that sequences of primitives serialized without delimiters!
|
1171 | value!(seq: vec![1, 2, 3] => "123" );
|
1172 | value!(seq_empty: Vec::<usize>::new());
|
1173 | value!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
1174 | => "<"&'> \
|
1175 | with \t\n\r spaces \
|
1176 | 3" );
|
1177 | value!(tuple_struct: Tuple("first" , 42) => "first42" );
|
1178 | value!(enum_tuple: Enum::Tuple("first" , 42)
|
1179 | => "<Tuple>first</Tuple> \
|
1180 | <Tuple>42</Tuple>" );
|
1181 |
|
1182 | // We cannot wrap map or struct in any container and should not
|
1183 | // flatten it, so it is impossible to serialize maps and structs
|
1184 | err!(map:
|
1185 | BTreeMap::from([("$value" , BTreeMap::from([("_1" , 2), ("_3" , 4)]))])
|
1186 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
1187 | err!(struct_:
|
1188 | BTreeMap::from([("$value" , Struct { key: "answer" , val: (42, 42) })])
|
1189 | => Unsupported("serialization of struct `Struct` is not supported in `$value` field" ));
|
1190 | value!(enum_struct:
|
1191 | Enum::Struct { key: "answer" , val: (42, 42) }
|
1192 | => "<Struct> \
|
1193 | <key>answer</key> \
|
1194 | <val>42</val> \
|
1195 | <val>42</val> \
|
1196 | </Struct>" );
|
1197 | }
|
1198 |
|
1199 | /// `$value` field inside a struct
|
1200 | mod struct_ {
|
1201 | use super::*;
|
1202 | use pretty_assertions::assert_eq;
|
1203 |
|
1204 | macro_rules! value {
|
1205 | ($name:ident: $data:expr => $expected:literal) => {
|
1206 | serialize_as!($name:
|
1207 | Value {
|
1208 | before: "answer" ,
|
1209 | content: $data,
|
1210 | after: "answer" ,
|
1211 | }
|
1212 | => concat!(
|
1213 | "<root><before>answer</before>" ,
|
1214 | $expected,
|
1215 | "<after>answer</after></root>" ,
|
1216 | ));
|
1217 | };
|
1218 | }
|
1219 |
|
1220 | value!(false_: false => "false" );
|
1221 | value!(true_: true => "true" );
|
1222 |
|
1223 | value!(i8_: -42i8 => "-42" );
|
1224 | value!(i16_: -4200i16 => "-4200" );
|
1225 | value!(i32_: -42000000i32 => "-42000000" );
|
1226 | value!(i64_: -42000000000000i64 => "-42000000000000" );
|
1227 | value!(isize_: -42000000000000isize => "-42000000000000" );
|
1228 |
|
1229 | value!(u8_: 42u8 => "42" );
|
1230 | value!(u16_: 4200u16 => "4200" );
|
1231 | value!(u32_: 42000000u32 => "42000000" );
|
1232 | value!(u64_: 42000000000000u64 => "42000000000000" );
|
1233 | value!(usize_: 42000000000000usize => "42000000000000" );
|
1234 |
|
1235 | serde_if_integer128! {
|
1236 | value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
1237 | value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
1238 | }
|
1239 |
|
1240 | value!(f32_: 4.2f32 => "4.2" );
|
1241 | value!(f64_: 4.2f64 => "4.2" );
|
1242 |
|
1243 | value!(char_non_escaped: 'h' => "h" );
|
1244 | value!(char_lt: '<' => "<" );
|
1245 | value!(char_gt: '>' => ">" );
|
1246 | value!(char_amp: '&' => "&" );
|
1247 | value!(char_apos: ' \'' => "'" );
|
1248 | value!(char_quot: '"' => """ );
|
1249 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
1250 | // pretty-print does not change the content
|
1251 | value!(char_space: ' ' => " " );
|
1252 |
|
1253 | value!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
1254 | value!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
1255 |
|
1256 | err!(bytes:
|
1257 | Value {
|
1258 | before: "answer" ,
|
1259 | content: Bytes(b"< \"escaped & bytes'>" ),
|
1260 | after: "answer" ,
|
1261 | }
|
1262 | => Unsupported("`serialize_bytes` not supported yet" ));
|
1263 |
|
1264 | value!(option_none: Option::<&str>::None => "" );
|
1265 | value!(option_some: Some("non-escaped string" ) => "non-escaped string" );
|
1266 | value!(option_some_empty_str: Some("" ) => "" );
|
1267 |
|
1268 | value!(unit: () => "" );
|
1269 | value!(unit_struct: Unit => "" );
|
1270 | value!(unit_struct_escaped: UnitEscaped => "" );
|
1271 |
|
1272 | value!(enum_unit: Enum::Unit => "<Unit/>" );
|
1273 | err!(enum_unit_escaped:
|
1274 | Value {
|
1275 | before: "answer" ,
|
1276 | content: Enum::UnitEscaped,
|
1277 | after: "answer" ,
|
1278 | }
|
1279 | => Unsupported("character `<` is not allowed at the start of an XML name `< \"&'>`" ));
|
1280 |
|
1281 | value!(newtype: Newtype(42) => "42" );
|
1282 | value!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>" );
|
1283 |
|
1284 | // Note that sequences of primitives serialized without delimiters!
|
1285 | value!(seq: vec![1, 2, 3] => "123" );
|
1286 | value!(seq_empty: Vec::<usize>::new() => "" );
|
1287 | value!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
1288 | => "<"&'> \
|
1289 | with \t\n\r spaces \
|
1290 | 3" );
|
1291 | value!(tuple_struct: Tuple("first" , 42) => "first42" );
|
1292 | value!(enum_tuple: Enum::Tuple("first" , 42)
|
1293 | => "<Tuple>first</Tuple> \
|
1294 | <Tuple>42</Tuple>" );
|
1295 |
|
1296 | // We cannot wrap map or struct in any container and should not
|
1297 | // flatten it, so it is impossible to serialize maps and structs
|
1298 | err!(map:
|
1299 | Value {
|
1300 | before: "answer" ,
|
1301 | content: BTreeMap::from([("_1" , 2), ("_3" , 4)]),
|
1302 | after: "answer" ,
|
1303 | }
|
1304 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
1305 | err!(struct_:
|
1306 | Value {
|
1307 | before: "answer" ,
|
1308 | content: Struct { key: "answer" , val: (42, 42) },
|
1309 | after: "answer" ,
|
1310 | }
|
1311 | => Unsupported("serialization of struct `Struct` is not supported in `$value` field" ));
|
1312 | value!(enum_struct:
|
1313 | Enum::Struct { key: "answer" , val: (42, 42) }
|
1314 | => "<Struct> \
|
1315 | <key>answer</key> \
|
1316 | <val>42</val> \
|
1317 | <val>42</val> \
|
1318 | </Struct>" );
|
1319 | }
|
1320 |
|
1321 | /// `$value` field inside a struct variant of an enum
|
1322 | mod enum_struct {
|
1323 | use super::*;
|
1324 | use pretty_assertions::assert_eq;
|
1325 |
|
1326 | macro_rules! value {
|
1327 | ($name:ident: $data:expr => $expected:literal) => {
|
1328 | serialize_as!($name:
|
1329 | SpecialEnum::Value {
|
1330 | before: "answer" ,
|
1331 | content: $data,
|
1332 | after: "answer" ,
|
1333 | }
|
1334 | => concat!(
|
1335 | "<Value><before>answer</before>" ,
|
1336 | $expected,
|
1337 | "<after>answer</after></Value>" ,
|
1338 | ));
|
1339 | };
|
1340 | }
|
1341 |
|
1342 | value!(false_: false => "false" );
|
1343 | value!(true_: true => "true" );
|
1344 |
|
1345 | value!(i8_: -42i8 => "-42" );
|
1346 | value!(i16_: -4200i16 => "-4200" );
|
1347 | value!(i32_: -42000000i32 => "-42000000" );
|
1348 | value!(i64_: -42000000000000i64 => "-42000000000000" );
|
1349 | value!(isize_: -42000000000000isize => "-42000000000000" );
|
1350 |
|
1351 | value!(u8_: 42u8 => "42" );
|
1352 | value!(u16_: 4200u16 => "4200" );
|
1353 | value!(u32_: 42000000u32 => "42000000" );
|
1354 | value!(u64_: 42000000000000u64 => "42000000000000" );
|
1355 | value!(usize_: 42000000000000usize => "42000000000000" );
|
1356 |
|
1357 | serde_if_integer128! {
|
1358 | value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
1359 | value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
1360 | }
|
1361 |
|
1362 | value!(f32_: 4.2f32 => "4.2" );
|
1363 | value!(f64_: 4.2f64 => "4.2" );
|
1364 |
|
1365 | value!(char_non_escaped: 'h' => "h" );
|
1366 | value!(char_lt: '<' => "<" );
|
1367 | value!(char_gt: '>' => ">" );
|
1368 | value!(char_amp: '&' => "&" );
|
1369 | value!(char_apos: ' \'' => "'" );
|
1370 | value!(char_quot: '"' => """ );
|
1371 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
1372 | // pretty-print does not change the content
|
1373 | value!(char_space: ' ' => " " );
|
1374 |
|
1375 | value!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
1376 | value!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
1377 |
|
1378 | err!(bytes:
|
1379 | SpecialEnum::Value {
|
1380 | before: "answer" ,
|
1381 | content: Bytes(b"< \"escaped & bytes'>" ),
|
1382 | after: "answer" ,
|
1383 | }
|
1384 | => Unsupported("`serialize_bytes` not supported yet" ));
|
1385 |
|
1386 | value!(option_none: Option::<&str>::None => "" );
|
1387 | value!(option_some: Some("non-escaped string" ) => "non-escaped string" );
|
1388 | value!(option_some_empty_str: Some("" ) => "" );
|
1389 |
|
1390 | value!(unit: () => "" );
|
1391 | value!(unit_struct: Unit => "" );
|
1392 | value!(unit_struct_escaped: UnitEscaped => "" );
|
1393 |
|
1394 | value!(enum_unit: Enum::Unit => "<Unit/>" );
|
1395 | err!(enum_unit_escaped:
|
1396 | SpecialEnum::Value {
|
1397 | before: "answer" ,
|
1398 | content: Enum::UnitEscaped,
|
1399 | after: "answer" ,
|
1400 | }
|
1401 | => Unsupported("character `<` is not allowed at the start of an XML name `< \"&'>`" ));
|
1402 |
|
1403 | value!(newtype: Newtype(42) => "42" );
|
1404 | value!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>" );
|
1405 |
|
1406 | // Note that sequences of primitives serialized without delimiters!
|
1407 | value!(seq: vec![1, 2, 3] => "123" );
|
1408 | value!(seq_empty: Vec::<usize>::new() => "" );
|
1409 | value!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
1410 | => "<"&'> \
|
1411 | with \t\n\r spaces \
|
1412 | 3" );
|
1413 | value!(tuple_struct: Tuple("first" , 42) => "first42" );
|
1414 | value!(enum_tuple: Enum::Tuple("first" , 42)
|
1415 | => "<Tuple>first</Tuple> \
|
1416 | <Tuple>42</Tuple>" );
|
1417 |
|
1418 | // We cannot wrap map or struct in any container and should not
|
1419 | // flatten it, so it is impossible to serialize maps and structs
|
1420 | err!(map:
|
1421 | SpecialEnum::Value {
|
1422 | before: "answer" ,
|
1423 | content: BTreeMap::from([("_1" , 2), ("_3" , 4)]),
|
1424 | after: "answer" ,
|
1425 | }
|
1426 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
1427 | err!(struct_:
|
1428 | SpecialEnum::Value {
|
1429 | before: "answer" ,
|
1430 | content: Struct { key: "answer" , val: (42, 42) },
|
1431 | after: "answer" ,
|
1432 | }
|
1433 | => Unsupported("serialization of struct `Struct` is not supported in `$value` field" ));
|
1434 | value!(enum_struct:
|
1435 | Enum::Struct { key: "answer" , val: (42, 42) }
|
1436 | => "<Struct> \
|
1437 | <key>answer</key> \
|
1438 | <val>42</val> \
|
1439 | <val>42</val> \
|
1440 | </Struct>" );
|
1441 | }
|
1442 | }
|
1443 |
|
1444 | mod attributes {
|
1445 | use super::*;
|
1446 | use pretty_assertions::assert_eq;
|
1447 |
|
1448 | serialize_as!(map_attr: BTreeMap::from([("@key1" , 1), ("@key2" , 2)])
|
1449 | => r#"<root key1="1" key2="2"/>"# );
|
1450 | serialize_as!(map_mixed: BTreeMap::from([("@key1" , 1), ("key2" , 2)])
|
1451 | => r#"<root key1="1"><key2>2</key2></root>"# );
|
1452 |
|
1453 | serialize_as!(struct_: Attributes { key: "answer" , val: (42, 42) }
|
1454 | => r#"<root key="answer" val="42 42"/>"# );
|
1455 | serialize_as!(struct_before: AttributesBefore { key: "answer" , val: 42 }
|
1456 | => r#"<root key="answer"><val>42</val></root>"# );
|
1457 | serialize_as!(struct_after: AttributesAfter { key: "answer" , val: 42 }
|
1458 | => r#"<root val="42"><key>answer</key></root>"# );
|
1459 |
|
1460 | serialize_as!(enum_: Enum::Attributes { key: "answer" , val: (42, 42) }
|
1461 | => r#"<Attributes key="answer" val="42 42"/>"# );
|
1462 | serialize_as!(enum_before: Enum::AttributesBefore { key: "answer" , val: 42 }
|
1463 | => r#"<AttributesBefore key="answer"><val>42</val></AttributesBefore>"# );
|
1464 | serialize_as!(enum_after: Enum::AttributesAfter { key: "answer" , val: 42 }
|
1465 | => r#"<AttributesAfter val="42"><key>answer</key></AttributesAfter>"# );
|
1466 |
|
1467 | /// Test for https://github.com/tafia/quick-xml/issues/252
|
1468 | mod optional {
|
1469 | use super::*;
|
1470 | use pretty_assertions::assert_eq;
|
1471 |
|
1472 | serialize_as!(none:
|
1473 | OptionalAttributes { a: None, b: None }
|
1474 | => r#"<root a=""/>"# );
|
1475 | serialize_as!(some_empty_str:
|
1476 | OptionalAttributes {
|
1477 | a: Some("" ),
|
1478 | b: Some("" ),
|
1479 | }
|
1480 | => r#"<root a="" b=""/>"# );
|
1481 | serialize_as!(some_non_empty:
|
1482 | OptionalAttributes {
|
1483 | a: Some("1" ),
|
1484 | b: Some("2" ),
|
1485 | }
|
1486 | => r#"<root a="1" b="2"/>"# );
|
1487 | }
|
1488 | }
|
1489 |
|
1490 | /// Test for https://github.com/tafia/quick-xml/issues/252
|
1491 | mod optional {
|
1492 | use super::*;
|
1493 | use pretty_assertions::assert_eq;
|
1494 |
|
1495 | serialize_as!(none:
|
1496 | OptionalElements { a: None, b: None }
|
1497 | => "<root> \
|
1498 | <a/> \
|
1499 | </root>" );
|
1500 | serialize_as!(some_empty_str:
|
1501 | OptionalElements {
|
1502 | a: Some("" ),
|
1503 | b: Some("" ),
|
1504 | }
|
1505 | => "<root> \
|
1506 | <a/> \
|
1507 | <b/> \
|
1508 | </root>" );
|
1509 | serialize_as!(some_non_empty:
|
1510 | OptionalElements {
|
1511 | a: Some("1" ),
|
1512 | b: Some("2" ),
|
1513 | }
|
1514 | => "<root> \
|
1515 | <a>1</a> \
|
1516 | <b>2</b> \
|
1517 | </root>" );
|
1518 | }
|
1519 | }
|
1520 |
|
1521 | mod with_indent {
|
1522 | use super::*;
|
1523 | use crate::se::content::tests::Struct;
|
1524 | use crate::writer::Indentation;
|
1525 | use pretty_assertions::assert_eq;
|
1526 |
|
1527 | /// Checks that given `$data` successfully serialized as `$expected`
|
1528 | macro_rules! serialize_as {
|
1529 | ($name:ident: $data:expr => $expected:expr) => {
|
1530 | #[test]
|
1531 | fn $name() {
|
1532 | let mut buffer = String::new();
|
1533 | let ser = ElementSerializer {
|
1534 | ser: ContentSerializer {
|
1535 | writer: &mut buffer,
|
1536 | level: QuoteLevel::Full,
|
1537 | indent: Indent::Owned(Indentation::new(b' ' , 2)),
|
1538 | write_indent: false,
|
1539 | expand_empty_elements: false,
|
1540 | },
|
1541 | key: XmlName("root" ),
|
1542 | };
|
1543 |
|
1544 | $data.serialize(ser).unwrap();
|
1545 | assert_eq!(buffer, $expected);
|
1546 | }
|
1547 | };
|
1548 | }
|
1549 |
|
1550 | /// Checks that attempt to serialize given `$data` results to a
|
1551 | /// serialization error `$kind` with `$reason`
|
1552 | macro_rules! err {
|
1553 | ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
|
1554 | #[test]
|
1555 | fn $name() {
|
1556 | let mut buffer = String::new();
|
1557 | let ser = ElementSerializer {
|
1558 | ser: ContentSerializer {
|
1559 | writer: &mut buffer,
|
1560 | level: QuoteLevel::Full,
|
1561 | indent: Indent::Owned(Indentation::new(b' ' , 2)),
|
1562 | write_indent: false,
|
1563 | expand_empty_elements: false,
|
1564 | },
|
1565 | key: XmlName("root" ),
|
1566 | };
|
1567 |
|
1568 | match $data.serialize(ser).unwrap_err() {
|
1569 | DeError::$kind(e) => assert_eq!(e, $reason),
|
1570 | e => panic!(
|
1571 | "Expected `{}({})`, found `{:?}`" ,
|
1572 | stringify!($kind),
|
1573 | $reason,
|
1574 | e
|
1575 | ),
|
1576 | }
|
1577 | // We can write something before fail
|
1578 | // assert_eq!(buffer, "");
|
1579 | }
|
1580 | };
|
1581 | }
|
1582 |
|
1583 | serialize_as!(false_: false => "<root>false</root>" );
|
1584 | serialize_as!(true_: true => "<root>true</root>" );
|
1585 |
|
1586 | serialize_as!(i8_: -42i8 => "<root>-42</root>" );
|
1587 | serialize_as!(i16_: -4200i16 => "<root>-4200</root>" );
|
1588 | serialize_as!(i32_: -42000000i32 => "<root>-42000000</root>" );
|
1589 | serialize_as!(i64_: -42000000000000i64 => "<root>-42000000000000</root>" );
|
1590 | serialize_as!(isize_: -42000000000000isize => "<root>-42000000000000</root>" );
|
1591 |
|
1592 | serialize_as!(u8_: 42u8 => "<root>42</root>" );
|
1593 | serialize_as!(u16_: 4200u16 => "<root>4200</root>" );
|
1594 | serialize_as!(u32_: 42000000u32 => "<root>42000000</root>" );
|
1595 | serialize_as!(u64_: 42000000000000u64 => "<root>42000000000000</root>" );
|
1596 | serialize_as!(usize_: 42000000000000usize => "<root>42000000000000</root>" );
|
1597 |
|
1598 | serde_if_integer128! {
|
1599 | serialize_as!(i128_: -420000000000000000000000000000i128 => "<root>-420000000000000000000000000000</root>" );
|
1600 | serialize_as!(u128_: 420000000000000000000000000000u128 => "<root>420000000000000000000000000000</root>" );
|
1601 | }
|
1602 |
|
1603 | serialize_as!(f32_: 4.2f32 => "<root>4.2</root>" );
|
1604 | serialize_as!(f64_: 4.2f64 => "<root>4.2</root>" );
|
1605 |
|
1606 | serialize_as!(char_non_escaped: 'h' => "<root>h</root>" );
|
1607 | serialize_as!(char_lt: '<' => "<root><</root>" );
|
1608 | serialize_as!(char_gt: '>' => "<root>></root>" );
|
1609 | serialize_as!(char_amp: '&' => "<root>&</root>" );
|
1610 | serialize_as!(char_apos: ' \'' => "<root>'</root>" );
|
1611 | serialize_as!(char_quot: '"' => "<root>"</root>" );
|
1612 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
1613 | // pretty-print does not change the content
|
1614 | serialize_as!(char_space: ' ' => "<root> </root>" );
|
1615 |
|
1616 | serialize_as!(str_non_escaped: "non-escaped string" => "<root>non-escaped string</root>" );
|
1617 | serialize_as!(str_escaped: "< \"escaped & string'>" => "<root><"escaped & string'></root>" );
|
1618 |
|
1619 | err!(bytes: Bytes(b"< \"escaped & bytes'>" ) => Unsupported("`serialize_bytes` not supported yet" ));
|
1620 |
|
1621 | serialize_as!(option_none: Option::<&str>::None => "<root/>" );
|
1622 | serialize_as!(option_some: Some("non-escaped string" ) => "<root>non-escaped string</root>" );
|
1623 | serialize_as!(option_some_empty: Some("" ) => "<root/>" );
|
1624 |
|
1625 | serialize_as!(unit: () => "<root/>" );
|
1626 | serialize_as!(unit_struct: Unit => "<root/>" );
|
1627 | serialize_as!(unit_struct_escaped: UnitEscaped => "<root/>" );
|
1628 |
|
1629 | serialize_as!(enum_unit: Enum::Unit => "<Unit/>" );
|
1630 | err!(enum_unit_escaped: Enum::UnitEscaped
|
1631 | => Unsupported("character `<` is not allowed at the start of an XML name `< \"&'>`" ));
|
1632 |
|
1633 | serialize_as!(newtype: Newtype(42) => "<root>42</root>" );
|
1634 | serialize_as!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>" );
|
1635 |
|
1636 | serialize_as!(seq: vec![1, 2, 3]
|
1637 | => "<root>1</root> \n\
|
1638 | <root>2</root> \n\
|
1639 | <root>3</root>" );
|
1640 | serialize_as!(seq_empty: Vec::<usize>::new() => "" );
|
1641 | serialize_as!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
1642 | => "<root><"&'></root> \n\
|
1643 | <root>with \t\n\r spaces</root> \n\
|
1644 | <root>3</root>" );
|
1645 | serialize_as!(tuple_struct: Tuple("first" , 42)
|
1646 | => "<root>first</root> \n\
|
1647 | <root>42</root>" );
|
1648 | serialize_as!(enum_tuple: Enum::Tuple("first" , 42)
|
1649 | => "<Tuple>first</Tuple> \n\
|
1650 | <Tuple>42</Tuple>" );
|
1651 |
|
1652 | serialize_as!(map: BTreeMap::from([("_1" , 2), ("_3" , 4)])
|
1653 | => "<root> \n \
|
1654 | <_1>2</_1> \n \
|
1655 | <_3>4</_3> \n\
|
1656 | </root>" );
|
1657 | serialize_as!(struct_: Struct { key: "answer" , val: (42, 42) }
|
1658 | => "<root> \n \
|
1659 | <key>answer</key> \n \
|
1660 | <val>42</val> \n \
|
1661 | <val>42</val> \n\
|
1662 | </root>" );
|
1663 | serialize_as!(enum_struct: Enum::Struct { key: "answer" , val: (42, 42) }
|
1664 | => "<Struct> \n \
|
1665 | <key>answer</key> \n \
|
1666 | <val>42</val> \n \
|
1667 | <val>42</val> \n\
|
1668 | </Struct>" );
|
1669 |
|
1670 | /// Special field name `$text` should be serialized as text content.
|
1671 | /// Sequences serialized as an `xs:list` content
|
1672 | mod text {
|
1673 | use super::*;
|
1674 |
|
1675 | /// `$text` key in a map
|
1676 | mod map {
|
1677 | use super::*;
|
1678 | use pretty_assertions::assert_eq;
|
1679 |
|
1680 | macro_rules! text {
|
1681 | ($name:ident: $data:expr) => {
|
1682 | serialize_as!($name:
|
1683 | BTreeMap::from([("$text" , $data)])
|
1684 | => "<root/>" );
|
1685 | };
|
1686 | ($name:ident: $data:expr => $expected:literal) => {
|
1687 | serialize_as!($name:
|
1688 | BTreeMap::from([("$text" , $data)])
|
1689 | => concat!("<root> \n " , $expected," \n</root>" ));
|
1690 | };
|
1691 | }
|
1692 |
|
1693 | text!(false_: false => "false" );
|
1694 | text!(true_: true => "true" );
|
1695 |
|
1696 | text!(i8_: -42i8 => "-42" );
|
1697 | text!(i16_: -4200i16 => "-4200" );
|
1698 | text!(i32_: -42000000i32 => "-42000000" );
|
1699 | text!(i64_: -42000000000000i64 => "-42000000000000" );
|
1700 | text!(isize_: -42000000000000isize => "-42000000000000" );
|
1701 |
|
1702 | text!(u8_: 42u8 => "42" );
|
1703 | text!(u16_: 4200u16 => "4200" );
|
1704 | text!(u32_: 42000000u32 => "42000000" );
|
1705 | text!(u64_: 42000000000000u64 => "42000000000000" );
|
1706 | text!(usize_: 42000000000000usize => "42000000000000" );
|
1707 |
|
1708 | serde_if_integer128! {
|
1709 | text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
1710 | text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
1711 | }
|
1712 |
|
1713 | text!(f32_: 4.2f32 => "4.2" );
|
1714 | text!(f64_: 4.2f64 => "4.2" );
|
1715 |
|
1716 | text!(char_non_escaped: 'h' => "h" );
|
1717 | text!(char_lt: '<' => "<" );
|
1718 | text!(char_gt: '>' => ">" );
|
1719 | text!(char_amp: '&' => "&" );
|
1720 | text!(char_apos: ' \'' => "'" );
|
1721 | text!(char_quot: '"' => """ );
|
1722 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
1723 | // pretty-print does not change the content
|
1724 | text!(char_space: ' ' => " " );
|
1725 |
|
1726 | text!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
1727 | text!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
1728 |
|
1729 | err!(bytes:
|
1730 | Text {
|
1731 | before: "answer" ,
|
1732 | content: Bytes(b"< \"escaped & bytes'>" ),
|
1733 | after: "answer" ,
|
1734 | }
|
1735 | => Unsupported("`serialize_bytes` not supported yet" ));
|
1736 |
|
1737 | text!(option_none: Option::<&str>::None);
|
1738 | text!(option_some: Some("non-escaped string" ) => "non-escaped string" );
|
1739 | text!(option_some_empty_str: Some("" ));
|
1740 |
|
1741 | text!(unit: ());
|
1742 | text!(unit_struct: Unit);
|
1743 | text!(unit_struct_escaped: UnitEscaped);
|
1744 |
|
1745 | text!(enum_unit: Enum::Unit => "Unit" );
|
1746 | text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>" );
|
1747 |
|
1748 | text!(newtype: Newtype(42) => "42" );
|
1749 | // We have no space where name of a variant can be stored
|
1750 | err!(enum_newtype:
|
1751 | Text {
|
1752 | before: "answer" ,
|
1753 | content: Enum::Newtype(42),
|
1754 | after: "answer" ,
|
1755 | }
|
1756 | => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an attribute or text content value" ));
|
1757 |
|
1758 | // Sequences are serialized separated by spaces, all spaces inside are escaped
|
1759 | text!(seq: vec![1, 2, 3] => "1 2 3" );
|
1760 | text!(seq_empty: Vec::<usize>::new());
|
1761 | text!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
1762 | => "<"&'> \
|
1763 | with	  spaces \
|
1764 | 3" );
|
1765 | text!(tuple_struct: Tuple("first" , 42) => "first 42" );
|
1766 | // We have no space where name of a variant can be stored
|
1767 | err!(enum_tuple:
|
1768 | Text {
|
1769 | before: "answer" ,
|
1770 | content: Enum::Tuple("first" , 42),
|
1771 | after: "answer" ,
|
1772 | }
|
1773 | => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an attribute or text content value" ));
|
1774 |
|
1775 | // Complex types cannot be serialized in `$text` field
|
1776 | err!(map:
|
1777 | Text {
|
1778 | before: "answer" ,
|
1779 | content: BTreeMap::from([("_1" , 2), ("_3" , 4)]),
|
1780 | after: "answer" ,
|
1781 | }
|
1782 | => Unsupported("map cannot be serialized as an attribute or text content value" ));
|
1783 | err!(struct_:
|
1784 | Text {
|
1785 | before: "answer" ,
|
1786 | content: Struct { key: "answer" , val: (42, 42) },
|
1787 | after: "answer" ,
|
1788 | }
|
1789 | => Unsupported("struct `Struct` cannot be serialized as an attribute or text content value" ));
|
1790 | err!(enum_struct:
|
1791 | Text {
|
1792 | before: "answer" ,
|
1793 | content: Enum::Struct { key: "answer" , val: (42, 42) },
|
1794 | after: "answer" ,
|
1795 | }
|
1796 | => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an attribute or text content value" ));
|
1797 | }
|
1798 |
|
1799 | /// `$text` field inside a struct
|
1800 | mod struct_ {
|
1801 | use super::*;
|
1802 | use pretty_assertions::assert_eq;
|
1803 |
|
1804 | macro_rules! text {
|
1805 | ($name:ident: $data:expr) => {
|
1806 | serialize_as!($name:
|
1807 | Text {
|
1808 | before: "answer" ,
|
1809 | content: $data,
|
1810 | after: "answer" ,
|
1811 | }
|
1812 | => "<root> \n \
|
1813 | <before>answer</before> \n \
|
1814 | <after>answer</after> \n\
|
1815 | </root>" );
|
1816 | };
|
1817 | ($name:ident: $data:expr => $expected:literal) => {
|
1818 | serialize_as!($name:
|
1819 | Text {
|
1820 | before: "answer" ,
|
1821 | content: $data,
|
1822 | after: "answer" ,
|
1823 | }
|
1824 | => concat!(
|
1825 | "<root> \n <before>answer</before> \n " ,
|
1826 | $expected,
|
1827 | " \n <after>answer</after> \n</root>" ,
|
1828 | ));
|
1829 | };
|
1830 | }
|
1831 |
|
1832 | text!(false_: false => "false" );
|
1833 | text!(true_: true => "true" );
|
1834 |
|
1835 | text!(i8_: -42i8 => "-42" );
|
1836 | text!(i16_: -4200i16 => "-4200" );
|
1837 | text!(i32_: -42000000i32 => "-42000000" );
|
1838 | text!(i64_: -42000000000000i64 => "-42000000000000" );
|
1839 | text!(isize_: -42000000000000isize => "-42000000000000" );
|
1840 |
|
1841 | text!(u8_: 42u8 => "42" );
|
1842 | text!(u16_: 4200u16 => "4200" );
|
1843 | text!(u32_: 42000000u32 => "42000000" );
|
1844 | text!(u64_: 42000000000000u64 => "42000000000000" );
|
1845 | text!(usize_: 42000000000000usize => "42000000000000" );
|
1846 |
|
1847 | serde_if_integer128! {
|
1848 | text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
1849 | text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
1850 | }
|
1851 |
|
1852 | text!(f32_: 4.2f32 => "4.2" );
|
1853 | text!(f64_: 4.2f64 => "4.2" );
|
1854 |
|
1855 | text!(char_non_escaped: 'h' => "h" );
|
1856 | text!(char_lt: '<' => "<" );
|
1857 | text!(char_gt: '>' => ">" );
|
1858 | text!(char_amp: '&' => "&" );
|
1859 | text!(char_apos: ' \'' => "'" );
|
1860 | text!(char_quot: '"' => """ );
|
1861 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
1862 | // pretty-print does not change the content
|
1863 | text!(char_space: ' ' => " " );
|
1864 |
|
1865 | text!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
1866 | text!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
1867 |
|
1868 | err!(bytes:
|
1869 | Text {
|
1870 | before: "answer" ,
|
1871 | content: Bytes(b"< \"escaped & bytes'>" ),
|
1872 | after: "answer" ,
|
1873 | }
|
1874 | => Unsupported("`serialize_bytes` not supported yet" ));
|
1875 |
|
1876 | text!(option_none: Option::<&str>::None);
|
1877 | text!(option_some: Some("non-escaped string" ) => "non-escaped string" );
|
1878 | text!(option_some_empty_str: Some("" ));
|
1879 |
|
1880 | text!(unit: ());
|
1881 | text!(unit_struct: Unit);
|
1882 | text!(unit_struct_escaped: UnitEscaped);
|
1883 |
|
1884 | text!(enum_unit: Enum::Unit => "Unit" );
|
1885 | text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>" );
|
1886 |
|
1887 | text!(newtype: Newtype(42) => "42" );
|
1888 | // We have no space where name of a variant can be stored
|
1889 | err!(enum_newtype:
|
1890 | Text {
|
1891 | before: "answer" ,
|
1892 | content: Enum::Newtype(42),
|
1893 | after: "answer" ,
|
1894 | }
|
1895 | => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an attribute or text content value" ));
|
1896 |
|
1897 | // Sequences are serialized separated by spaces, all spaces inside are escaped
|
1898 | text!(seq: vec![1, 2, 3] => "1 2 3" );
|
1899 | text!(seq_empty: Vec::<usize>::new());
|
1900 | text!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
1901 | => "<"&'> \
|
1902 | with	  spaces \
|
1903 | 3" );
|
1904 | text!(tuple_struct: Tuple("first" , 42) => "first 42" );
|
1905 | // We have no space where name of a variant can be stored
|
1906 | err!(enum_tuple:
|
1907 | Text {
|
1908 | before: "answer" ,
|
1909 | content: Enum::Tuple("first" , 42),
|
1910 | after: "answer" ,
|
1911 | }
|
1912 | => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an attribute or text content value" ));
|
1913 |
|
1914 | // Complex types cannot be serialized in `$text` field
|
1915 | err!(map:
|
1916 | Text {
|
1917 | before: "answer" ,
|
1918 | content: BTreeMap::from([("_1" , 2), ("_3" , 4)]),
|
1919 | after: "answer" ,
|
1920 | }
|
1921 | => Unsupported("map cannot be serialized as an attribute or text content value" ));
|
1922 | err!(struct_:
|
1923 | Text {
|
1924 | before: "answer" ,
|
1925 | content: Struct { key: "answer" , val: (42, 42) },
|
1926 | after: "answer" ,
|
1927 | }
|
1928 | => Unsupported("struct `Struct` cannot be serialized as an attribute or text content value" ));
|
1929 | err!(enum_struct:
|
1930 | Text {
|
1931 | before: "answer" ,
|
1932 | content: Enum::Struct { key: "answer" , val: (42, 42) },
|
1933 | after: "answer" ,
|
1934 | }
|
1935 | => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an attribute or text content value" ));
|
1936 | }
|
1937 |
|
1938 | /// `$text` field inside a struct variant of an enum
|
1939 | mod enum_struct {
|
1940 | use super::*;
|
1941 | use pretty_assertions::assert_eq;
|
1942 |
|
1943 | macro_rules! text {
|
1944 | ($name:ident: $data:expr) => {
|
1945 | serialize_as!($name:
|
1946 | SpecialEnum::Text {
|
1947 | before: "answer" ,
|
1948 | content: $data,
|
1949 | after: "answer" ,
|
1950 | }
|
1951 | => "<Text> \n \
|
1952 | <before>answer</before> \n \
|
1953 | <after>answer</after> \n\
|
1954 | </Text>" );
|
1955 | };
|
1956 | ($name:ident: $data:expr => $expected:literal) => {
|
1957 | serialize_as!($name:
|
1958 | SpecialEnum::Text {
|
1959 | before: "answer" ,
|
1960 | content: $data,
|
1961 | after: "answer" ,
|
1962 | }
|
1963 | => concat!(
|
1964 | "<Text> \n <before>answer</before> \n " ,
|
1965 | $expected,
|
1966 | " \n <after>answer</after> \n</Text>" ,
|
1967 | ));
|
1968 | };
|
1969 | }
|
1970 |
|
1971 | text!(false_: false => "false" );
|
1972 | text!(true_: true => "true" );
|
1973 |
|
1974 | text!(i8_: -42i8 => "-42" );
|
1975 | text!(i16_: -4200i16 => "-4200" );
|
1976 | text!(i32_: -42000000i32 => "-42000000" );
|
1977 | text!(i64_: -42000000000000i64 => "-42000000000000" );
|
1978 | text!(isize_: -42000000000000isize => "-42000000000000" );
|
1979 |
|
1980 | text!(u8_: 42u8 => "42" );
|
1981 | text!(u16_: 4200u16 => "4200" );
|
1982 | text!(u32_: 42000000u32 => "42000000" );
|
1983 | text!(u64_: 42000000000000u64 => "42000000000000" );
|
1984 | text!(usize_: 42000000000000usize => "42000000000000" );
|
1985 |
|
1986 | serde_if_integer128! {
|
1987 | text!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
1988 | text!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
1989 | }
|
1990 |
|
1991 | text!(f32_: 4.2f32 => "4.2" );
|
1992 | text!(f64_: 4.2f64 => "4.2" );
|
1993 |
|
1994 | text!(char_non_escaped: 'h' => "h" );
|
1995 | text!(char_lt: '<' => "<" );
|
1996 | text!(char_gt: '>' => ">" );
|
1997 | text!(char_amp: '&' => "&" );
|
1998 | text!(char_apos: ' \'' => "'" );
|
1999 | text!(char_quot: '"' => """ );
|
2000 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
2001 | // pretty-print does not change the content
|
2002 | text!(char_space: ' ' => " " );
|
2003 |
|
2004 | text!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
2005 | text!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
2006 |
|
2007 | err!(bytes:
|
2008 | SpecialEnum::Text {
|
2009 | before: "answer" ,
|
2010 | content: Bytes(b"< \"escaped & bytes'>" ),
|
2011 | after: "answer" ,
|
2012 | }
|
2013 | => Unsupported("`serialize_bytes` not supported yet" ));
|
2014 |
|
2015 | text!(option_none: Option::<&str>::None);
|
2016 | text!(option_some: Some("non-escaped string" ) => "non-escaped string" );
|
2017 | text!(option_some_empty_str: Some("" ));
|
2018 |
|
2019 | text!(unit: ());
|
2020 | text!(unit_struct: Unit);
|
2021 | text!(unit_struct_escaped: UnitEscaped);
|
2022 |
|
2023 | text!(enum_unit: Enum::Unit => "Unit" );
|
2024 | text!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>" );
|
2025 |
|
2026 | text!(newtype: Newtype(42) => "42" );
|
2027 | // We have no space where name of a variant can be stored
|
2028 | err!(enum_newtype:
|
2029 | SpecialEnum::Text {
|
2030 | before: "answer" ,
|
2031 | content: Enum::Newtype(42),
|
2032 | after: "answer" ,
|
2033 | }
|
2034 | => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an attribute or text content value" ));
|
2035 |
|
2036 | // Sequences are serialized separated by spaces, all spaces inside are escaped
|
2037 | text!(seq: vec![1, 2, 3] => "1 2 3" );
|
2038 | text!(seq_empty: Vec::<usize>::new());
|
2039 | text!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
2040 | => "<"&'> \
|
2041 | with	  spaces \
|
2042 | 3" );
|
2043 | text!(tuple_struct: Tuple("first" , 42) => "first 42" );
|
2044 | // We have no space where name of a variant can be stored
|
2045 | err!(enum_tuple:
|
2046 | SpecialEnum::Text {
|
2047 | before: "answer" ,
|
2048 | content: Enum::Tuple("first" , 42),
|
2049 | after: "answer" ,
|
2050 | }
|
2051 | => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an attribute or text content value" ));
|
2052 |
|
2053 | // Complex types cannot be serialized in `$text` field
|
2054 | err!(map:
|
2055 | SpecialEnum::Text {
|
2056 | before: "answer" ,
|
2057 | content: BTreeMap::from([("_1" , 2), ("_3" , 4)]),
|
2058 | after: "answer" ,
|
2059 | }
|
2060 | => Unsupported("map cannot be serialized as an attribute or text content value" ));
|
2061 | err!(struct_:
|
2062 | SpecialEnum::Text {
|
2063 | before: "answer" ,
|
2064 | content: Struct { key: "answer" , val: (42, 42) },
|
2065 | after: "answer" ,
|
2066 | }
|
2067 | => Unsupported("struct `Struct` cannot be serialized as an attribute or text content value" ));
|
2068 | err!(enum_struct:
|
2069 | SpecialEnum::Text {
|
2070 | before: "answer" ,
|
2071 | content: Enum::Struct { key: "answer" , val: (42, 42) },
|
2072 | after: "answer" ,
|
2073 | }
|
2074 | => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an attribute or text content value" ));
|
2075 | }
|
2076 | }
|
2077 |
|
2078 | /// Special field name `$value` should be serialized using name, provided
|
2079 | /// by the type of value instead of a key. Sequences serialized as a list
|
2080 | /// of tags with that name (each element can have their own name)
|
2081 | mod value {
|
2082 | use super::*;
|
2083 |
|
2084 | /// `$value` key in a map
|
2085 | mod map {
|
2086 | use super::*;
|
2087 | use pretty_assertions::assert_eq;
|
2088 |
|
2089 | macro_rules! value {
|
2090 | ($name:ident: $data:expr) => {
|
2091 | serialize_as!($name:
|
2092 | BTreeMap::from([("$value" , $data)])
|
2093 | => "<root/>" );
|
2094 | };
|
2095 | ($name:ident: $data:expr => $expected:literal) => {
|
2096 | serialize_as!($name:
|
2097 | BTreeMap::from([("$value" , $data)])
|
2098 | => concat!("<root> \n " , $expected," \n</root>" ));
|
2099 | };
|
2100 | }
|
2101 |
|
2102 | value!(false_: false => "false" );
|
2103 | value!(true_: true => "true" );
|
2104 |
|
2105 | value!(i8_: -42i8 => "-42" );
|
2106 | value!(i16_: -4200i16 => "-4200" );
|
2107 | value!(i32_: -42000000i32 => "-42000000" );
|
2108 | value!(i64_: -42000000000000i64 => "-42000000000000" );
|
2109 | value!(isize_: -42000000000000isize => "-42000000000000" );
|
2110 |
|
2111 | value!(u8_: 42u8 => "42" );
|
2112 | value!(u16_: 4200u16 => "4200" );
|
2113 | value!(u32_: 42000000u32 => "42000000" );
|
2114 | value!(u64_: 42000000000000u64 => "42000000000000" );
|
2115 | value!(usize_: 42000000000000usize => "42000000000000" );
|
2116 |
|
2117 | serde_if_integer128! {
|
2118 | value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
2119 | value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
2120 | }
|
2121 |
|
2122 | value!(f32_: 4.2f32 => "4.2" );
|
2123 | value!(f64_: 4.2f64 => "4.2" );
|
2124 |
|
2125 | value!(char_non_escaped: 'h' => "h" );
|
2126 | value!(char_lt: '<' => "<" );
|
2127 | value!(char_gt: '>' => ">" );
|
2128 | value!(char_amp: '&' => "&" );
|
2129 | value!(char_apos: ' \'' => "'" );
|
2130 | value!(char_quot: '"' => """ );
|
2131 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
2132 | // pretty-print does not change the content
|
2133 | value!(char_space: ' ' => " " );
|
2134 |
|
2135 | value!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
2136 | value!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
2137 |
|
2138 | err!(bytes:
|
2139 | BTreeMap::from([("$value" , Bytes(b"< \"escaped & bytes'>" ))])
|
2140 | => Unsupported("`serialize_bytes` not supported yet" ));
|
2141 |
|
2142 | value!(option_none: Option::<&str>::None);
|
2143 | value!(option_some: Some("non-escaped string" ) => "non-escaped string" );
|
2144 | value!(option_some_empty_str: Some("" ));
|
2145 |
|
2146 | value!(unit: ());
|
2147 | value!(unit_struct: Unit);
|
2148 | value!(unit_struct_escaped: UnitEscaped);
|
2149 |
|
2150 | value!(enum_unit: Enum::Unit => "<Unit/>" );
|
2151 | err!(enum_unit_escaped:
|
2152 | BTreeMap::from([("$value" , Enum::UnitEscaped)])
|
2153 | => Unsupported("character `<` is not allowed at the start of an XML name `< \"&'>`" ));
|
2154 |
|
2155 | value!(newtype: Newtype(42) => "42" );
|
2156 | value!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>" );
|
2157 |
|
2158 | value!(seq: vec![1, 2, 3] => "1 \n 2 \n 3" );
|
2159 | value!(seq_empty: Vec::<usize>::new());
|
2160 | value!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
2161 | => "<"&'> \n \
|
2162 | with \t\n\r spaces \n \
|
2163 | 3" );
|
2164 | value!(tuple_struct: Tuple("first" , 42) => "first \n 42" );
|
2165 | value!(enum_tuple: Enum::Tuple("first" , 42)
|
2166 | => "<Tuple>first</Tuple> \n \
|
2167 | <Tuple>42</Tuple>" );
|
2168 |
|
2169 | // We cannot wrap map or struct in any container and should not
|
2170 | // flatten it, so it is impossible to serialize maps and structs
|
2171 | err!(map:
|
2172 | BTreeMap::from([("$value" , BTreeMap::from([("_1" , 2), ("_3" , 4)]))])
|
2173 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
2174 | err!(struct_:
|
2175 | BTreeMap::from([("$value" , Struct { key: "answer" , val: (42, 42) })])
|
2176 | => Unsupported("serialization of struct `Struct` is not supported in `$value` field" ));
|
2177 | value!(enum_struct:
|
2178 | Enum::Struct { key: "answer" , val: (42, 42) }
|
2179 | => "<Struct> \n \
|
2180 | <key>answer</key> \n \
|
2181 | <val>42</val> \n \
|
2182 | <val>42</val> \n \
|
2183 | </Struct>" );
|
2184 | }
|
2185 |
|
2186 | /// `$value` field inside a struct
|
2187 | mod struct_ {
|
2188 | use super::*;
|
2189 | use pretty_assertions::assert_eq;
|
2190 |
|
2191 | macro_rules! value {
|
2192 | ($name:ident: $data:expr) => {
|
2193 | serialize_as!($name:
|
2194 | Value {
|
2195 | before: "answer" ,
|
2196 | content: $data,
|
2197 | after: "answer" ,
|
2198 | }
|
2199 | => "<root> \n \
|
2200 | <before>answer</before> \n \
|
2201 | <after>answer</after> \n\
|
2202 | </root>" );
|
2203 | };
|
2204 | ($name:ident: $data:expr => $expected:literal) => {
|
2205 | serialize_as!($name:
|
2206 | Value {
|
2207 | before: "answer" ,
|
2208 | content: $data,
|
2209 | after: "answer" ,
|
2210 | }
|
2211 | => concat!(
|
2212 | "<root> \n <before>answer</before> \n " ,
|
2213 | $expected,
|
2214 | " \n <after>answer</after> \n</root>" ,
|
2215 | ));
|
2216 | };
|
2217 | }
|
2218 |
|
2219 | value!(false_: false => "false" );
|
2220 | value!(true_: true => "true" );
|
2221 |
|
2222 | value!(i8_: -42i8 => "-42" );
|
2223 | value!(i16_: -4200i16 => "-4200" );
|
2224 | value!(i32_: -42000000i32 => "-42000000" );
|
2225 | value!(i64_: -42000000000000i64 => "-42000000000000" );
|
2226 | value!(isize_: -42000000000000isize => "-42000000000000" );
|
2227 |
|
2228 | value!(u8_: 42u8 => "42" );
|
2229 | value!(u16_: 4200u16 => "4200" );
|
2230 | value!(u32_: 42000000u32 => "42000000" );
|
2231 | value!(u64_: 42000000000000u64 => "42000000000000" );
|
2232 | value!(usize_: 42000000000000usize => "42000000000000" );
|
2233 |
|
2234 | serde_if_integer128! {
|
2235 | value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
2236 | value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
2237 | }
|
2238 |
|
2239 | value!(f32_: 4.2f32 => "4.2" );
|
2240 | value!(f64_: 4.2f64 => "4.2" );
|
2241 |
|
2242 | value!(char_non_escaped: 'h' => "h" );
|
2243 | value!(char_lt: '<' => "<" );
|
2244 | value!(char_gt: '>' => ">" );
|
2245 | value!(char_amp: '&' => "&" );
|
2246 | value!(char_apos: ' \'' => "'" );
|
2247 | value!(char_quot: '"' => """ );
|
2248 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
2249 | // pretty-print does not change the content
|
2250 | value!(char_space: ' ' => " " );
|
2251 |
|
2252 | value!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
2253 | value!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
2254 |
|
2255 | err!(bytes:
|
2256 | Value {
|
2257 | before: "answer" ,
|
2258 | content: Bytes(b"< \"escaped & bytes'>" ),
|
2259 | after: "answer" ,
|
2260 | }
|
2261 | => Unsupported("`serialize_bytes` not supported yet" ));
|
2262 |
|
2263 | value!(option_none: Option::<&str>::None);
|
2264 | value!(option_some: Some("non-escaped string" ) => "non-escaped string" );
|
2265 | value!(option_some_empty_str: Some("" ));
|
2266 |
|
2267 | value!(unit: ());
|
2268 | value!(unit_struct: Unit);
|
2269 | value!(unit_struct_escaped: UnitEscaped);
|
2270 |
|
2271 | value!(enum_unit: Enum::Unit => "<Unit/>" );
|
2272 | err!(enum_unit_escaped:
|
2273 | Value {
|
2274 | before: "answer" ,
|
2275 | content: Enum::UnitEscaped,
|
2276 | after: "answer" ,
|
2277 | }
|
2278 | => Unsupported("character `<` is not allowed at the start of an XML name `< \"&'>`" ));
|
2279 |
|
2280 | value!(newtype: Newtype(42) => "42" );
|
2281 | value!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>" );
|
2282 |
|
2283 | // Note that sequences of primitives serialized without delimiters!
|
2284 | value!(seq: vec![1, 2, 3] => "1 \n 2 \n 3" );
|
2285 | value!(seq_empty: Vec::<usize>::new());
|
2286 | value!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
2287 | => "<"&'> \n \
|
2288 | with \t\n\r spaces \n \
|
2289 | 3" );
|
2290 | value!(tuple_struct: Tuple("first" , 42) => "first \n 42" );
|
2291 | value!(enum_tuple: Enum::Tuple("first" , 42)
|
2292 | => "<Tuple>first</Tuple> \n \
|
2293 | <Tuple>42</Tuple>" );
|
2294 |
|
2295 | // We cannot wrap map or struct in any container and should not
|
2296 | // flatten it, so it is impossible to serialize maps and structs
|
2297 | err!(map:
|
2298 | Value {
|
2299 | before: "answer" ,
|
2300 | content: BTreeMap::from([("_1" , 2), ("_3" , 4)]),
|
2301 | after: "answer" ,
|
2302 | }
|
2303 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
2304 | err!(struct_:
|
2305 | Value {
|
2306 | before: "answer" ,
|
2307 | content: Struct { key: "answer" , val: (42, 42) },
|
2308 | after: "answer" ,
|
2309 | }
|
2310 | => Unsupported("serialization of struct `Struct` is not supported in `$value` field" ));
|
2311 | value!(enum_struct:
|
2312 | Enum::Struct { key: "answer" , val: (42, 42) }
|
2313 | => "<Struct> \n \
|
2314 | <key>answer</key> \n \
|
2315 | <val>42</val> \n \
|
2316 | <val>42</val> \n \
|
2317 | </Struct>" );
|
2318 | }
|
2319 |
|
2320 | /// `$value` field inside a struct variant of an enum
|
2321 | mod enum_struct {
|
2322 | use super::*;
|
2323 | use pretty_assertions::assert_eq;
|
2324 |
|
2325 | macro_rules! value {
|
2326 | ($name:ident: $data:expr) => {
|
2327 | serialize_as!($name:
|
2328 | SpecialEnum::Value {
|
2329 | before: "answer" ,
|
2330 | content: $data,
|
2331 | after: "answer" ,
|
2332 | }
|
2333 | => "<Value> \n \
|
2334 | <before>answer</before> \n \
|
2335 | <after>answer</after> \n\
|
2336 | </Value>" );
|
2337 | };
|
2338 | ($name:ident: $data:expr => $expected:literal) => {
|
2339 | serialize_as!($name:
|
2340 | SpecialEnum::Value {
|
2341 | before: "answer" ,
|
2342 | content: $data,
|
2343 | after: "answer" ,
|
2344 | }
|
2345 | => concat!(
|
2346 | "<Value> \n <before>answer</before> \n " ,
|
2347 | $expected,
|
2348 | " \n <after>answer</after> \n</Value>" ,
|
2349 | ));
|
2350 | };
|
2351 | }
|
2352 |
|
2353 | value!(false_: false => "false" );
|
2354 | value!(true_: true => "true" );
|
2355 |
|
2356 | value!(i8_: -42i8 => "-42" );
|
2357 | value!(i16_: -4200i16 => "-4200" );
|
2358 | value!(i32_: -42000000i32 => "-42000000" );
|
2359 | value!(i64_: -42000000000000i64 => "-42000000000000" );
|
2360 | value!(isize_: -42000000000000isize => "-42000000000000" );
|
2361 |
|
2362 | value!(u8_: 42u8 => "42" );
|
2363 | value!(u16_: 4200u16 => "4200" );
|
2364 | value!(u32_: 42000000u32 => "42000000" );
|
2365 | value!(u64_: 42000000000000u64 => "42000000000000" );
|
2366 | value!(usize_: 42000000000000usize => "42000000000000" );
|
2367 |
|
2368 | serde_if_integer128! {
|
2369 | value!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
2370 | value!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
2371 | }
|
2372 |
|
2373 | value!(f32_: 4.2f32 => "4.2" );
|
2374 | value!(f64_: 4.2f64 => "4.2" );
|
2375 |
|
2376 | value!(char_non_escaped: 'h' => "h" );
|
2377 | value!(char_lt: '<' => "<" );
|
2378 | value!(char_gt: '>' => ">" );
|
2379 | value!(char_amp: '&' => "&" );
|
2380 | value!(char_apos: ' \'' => "'" );
|
2381 | value!(char_quot: '"' => """ );
|
2382 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
2383 | // pretty-print does not change the content
|
2384 | value!(char_space: ' ' => " " );
|
2385 |
|
2386 | value!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
2387 | value!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
2388 |
|
2389 | err!(bytes:
|
2390 | SpecialEnum::Value {
|
2391 | before: "answer" ,
|
2392 | content: Bytes(b"< \"escaped & bytes'>" ),
|
2393 | after: "answer" ,
|
2394 | }
|
2395 | => Unsupported("`serialize_bytes` not supported yet" ));
|
2396 |
|
2397 | value!(option_none: Option::<&str>::None);
|
2398 | value!(option_some: Some("non-escaped string" ) => "non-escaped string" );
|
2399 | value!(option_some_empty_str: Some("" ));
|
2400 |
|
2401 | value!(unit: ());
|
2402 | value!(unit_struct: Unit);
|
2403 | value!(unit_struct_escaped: UnitEscaped);
|
2404 |
|
2405 | value!(enum_unit: Enum::Unit => "<Unit/>" );
|
2406 | err!(enum_unit_escaped:
|
2407 | SpecialEnum::Value {
|
2408 | before: "answer" ,
|
2409 | content: Enum::UnitEscaped,
|
2410 | after: "answer" ,
|
2411 | }
|
2412 | => Unsupported("character `<` is not allowed at the start of an XML name `< \"&'>`" ));
|
2413 |
|
2414 | value!(newtype: Newtype(42) => "42" );
|
2415 | value!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>" );
|
2416 |
|
2417 | // Note that sequences of primitives serialized without delimiters!
|
2418 | value!(seq: vec![1, 2, 3] => "1 \n 2 \n 3" );
|
2419 | value!(seq_empty: Vec::<usize>::new());
|
2420 | value!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
2421 | => "<"&'> \n \
|
2422 | with \t\n\r spaces \n \
|
2423 | 3" );
|
2424 | value!(tuple_struct: Tuple("first" , 42) => "first \n 42" );
|
2425 | value!(enum_tuple: Enum::Tuple("first" , 42)
|
2426 | => "<Tuple>first</Tuple> \n \
|
2427 | <Tuple>42</Tuple>" );
|
2428 |
|
2429 | // We cannot wrap map or struct in any container and should not
|
2430 | // flatten it, so it is impossible to serialize maps and structs
|
2431 | err!(map:
|
2432 | SpecialEnum::Value {
|
2433 | before: "answer" ,
|
2434 | content: BTreeMap::from([("_1" , 2), ("_3" , 4)]),
|
2435 | after: "answer" ,
|
2436 | }
|
2437 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
2438 | err!(struct_:
|
2439 | SpecialEnum::Value {
|
2440 | before: "answer" ,
|
2441 | content: Struct { key: "answer" , val: (42, 42) },
|
2442 | after: "answer" ,
|
2443 | }
|
2444 | => Unsupported("serialization of struct `Struct` is not supported in `$value` field" ));
|
2445 | value!(enum_struct:
|
2446 | Enum::Struct { key: "answer" , val: (42, 42) }
|
2447 | => "<Struct> \n \
|
2448 | <key>answer</key> \n \
|
2449 | <val>42</val> \n \
|
2450 | <val>42</val> \n \
|
2451 | </Struct>" );
|
2452 | }
|
2453 | }
|
2454 |
|
2455 | mod attributes {
|
2456 | use super::*;
|
2457 | use pretty_assertions::assert_eq;
|
2458 |
|
2459 | serialize_as!(map_attr: BTreeMap::from([("@key1" , 1), ("@key2" , 2)])
|
2460 | => r#"<root key1="1" key2="2"/>"# );
|
2461 | serialize_as!(map_mixed: BTreeMap::from([("@key1" , 1), ("key2" , 2)])
|
2462 | => "<root key1= \"1 \"> \n \
|
2463 | <key2>2</key2> \n\
|
2464 | </root>" );
|
2465 |
|
2466 | serialize_as!(struct_: Attributes { key: "answer" , val: (42, 42) }
|
2467 | => r#"<root key="answer" val="42 42"/>"# );
|
2468 | serialize_as!(struct_before: AttributesBefore { key: "answer" , val: 42 }
|
2469 | => "<root key= \"answer \"> \n \
|
2470 | <val>42</val> \n\
|
2471 | </root>" );
|
2472 | serialize_as!(struct_after: AttributesAfter { key: "answer" , val: 42 }
|
2473 | => "<root val= \"42 \"> \n \
|
2474 | <key>answer</key> \n\
|
2475 | </root>" );
|
2476 |
|
2477 | serialize_as!(enum_: Enum::Attributes { key: "answer" , val: (42, 42) }
|
2478 | => r#"<Attributes key="answer" val="42 42"/>"# );
|
2479 | serialize_as!(enum_before: Enum::AttributesBefore { key: "answer" , val: 42 }
|
2480 | => "<AttributesBefore key= \"answer \"> \n \
|
2481 | <val>42</val> \n\
|
2482 | </AttributesBefore>" );
|
2483 | serialize_as!(enum_after: Enum::AttributesAfter { key: "answer" , val: 42 }
|
2484 | => "<AttributesAfter val= \"42 \"> \n \
|
2485 | <key>answer</key> \n\
|
2486 | </AttributesAfter>" );
|
2487 |
|
2488 | /// Test for https://github.com/tafia/quick-xml/issues/252
|
2489 | mod optional {
|
2490 | use super::*;
|
2491 | use pretty_assertions::assert_eq;
|
2492 |
|
2493 | serialize_as!(none:
|
2494 | OptionalAttributes { a: None, b: None }
|
2495 | => r#"<root a=""/>"# );
|
2496 | serialize_as!(some_empty_str:
|
2497 | OptionalAttributes {
|
2498 | a: Some("" ),
|
2499 | b: Some("" )
|
2500 | }
|
2501 | => r#"<root a="" b=""/>"# );
|
2502 | serialize_as!(some_non_empty:
|
2503 | OptionalAttributes {
|
2504 | a: Some("a" ),
|
2505 | b: Some("b" )
|
2506 | }
|
2507 | => r#"<root a="a" b="b"/>"# );
|
2508 | }
|
2509 | }
|
2510 |
|
2511 | /// Test for https://github.com/tafia/quick-xml/issues/252
|
2512 | mod optional {
|
2513 | use super::*;
|
2514 | use pretty_assertions::assert_eq;
|
2515 |
|
2516 | serialize_as!(none:
|
2517 | OptionalElements { a: None, b: None }
|
2518 | => "<root> \n \
|
2519 | <a/> \n\
|
2520 | </root>" );
|
2521 | serialize_as!(some_empty_str:
|
2522 | OptionalElements {
|
2523 | a: Some("" ),
|
2524 | b: Some("" )
|
2525 | }
|
2526 | => "<root> \n \
|
2527 | <a/> \n \
|
2528 | <b/> \n\
|
2529 | </root>" );
|
2530 | serialize_as!(some_non_empty:
|
2531 | OptionalElements {
|
2532 | a: Some("a" ),
|
2533 | b: Some("b" )
|
2534 | }
|
2535 | => "<root> \n \
|
2536 | <a>a</a> \n \
|
2537 | <b>b</b> \n\
|
2538 | </root>" );
|
2539 | }
|
2540 | }
|
2541 |
|
2542 | mod expand_empty_elements {
|
2543 | use super::*;
|
2544 | use pretty_assertions::assert_eq;
|
2545 |
|
2546 | /// Checks that given `$data` successfully serialized as `$expected`
|
2547 | macro_rules! serialize_as {
|
2548 | ($name:ident: $data:expr => $expected:expr) => {
|
2549 | #[test]
|
2550 | fn $name() {
|
2551 | let mut buffer = String::new();
|
2552 | let ser = ElementSerializer {
|
2553 | ser: ContentSerializer {
|
2554 | writer: &mut buffer,
|
2555 | level: QuoteLevel::Full,
|
2556 | indent: Indent::None,
|
2557 | write_indent: false,
|
2558 | expand_empty_elements: true,
|
2559 | },
|
2560 | key: XmlName("root" ),
|
2561 | };
|
2562 |
|
2563 | $data.serialize(ser).unwrap();
|
2564 | assert_eq!(buffer, $expected);
|
2565 | }
|
2566 | };
|
2567 | }
|
2568 |
|
2569 | /// Checks that attempt to serialize given `$data` results to a
|
2570 | /// serialization error `$kind` with `$reason`
|
2571 | macro_rules! err {
|
2572 | ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
|
2573 | #[test]
|
2574 | fn $name() {
|
2575 | let mut buffer = String::new();
|
2576 | let ser = ElementSerializer {
|
2577 | ser: ContentSerializer {
|
2578 | writer: &mut buffer,
|
2579 | level: QuoteLevel::Full,
|
2580 | indent: Indent::None,
|
2581 | write_indent: false,
|
2582 | expand_empty_elements: false,
|
2583 | },
|
2584 | key: XmlName("root" ),
|
2585 | };
|
2586 |
|
2587 | match $data.serialize(ser).unwrap_err() {
|
2588 | DeError::$kind(e) => assert_eq!(e, $reason),
|
2589 | e => panic!(
|
2590 | "Expected `{}({})`, found `{:?}`" ,
|
2591 | stringify!($kind),
|
2592 | $reason,
|
2593 | e
|
2594 | ),
|
2595 | }
|
2596 | // We can write something before fail
|
2597 | // assert_eq!(buffer, "");
|
2598 | }
|
2599 | };
|
2600 | }
|
2601 |
|
2602 | serialize_as!(option_some_empty: Some("" ) => "<root></root>" );
|
2603 | serialize_as!(option_some_empty_str: Some("" ) => "<root></root>" );
|
2604 |
|
2605 | serialize_as!(unit: () => "<root></root>" );
|
2606 | serialize_as!(unit_struct: Unit => "<root></root>" );
|
2607 | serialize_as!(unit_struct_escaped: UnitEscaped => "<root></root>" );
|
2608 |
|
2609 | serialize_as!(enum_unit: Enum::Unit => "<Unit></Unit>" );
|
2610 | err!(enum_unit_escaped: Enum::UnitEscaped
|
2611 | => Unsupported("character `<` is not allowed at the start of an XML name `< \"&'>`" ));
|
2612 | }
|
2613 | }
|
2614 | |