1//! Contains Serde `Serializer` for XML [simple types] [as defined] in the XML Schema.
2//!
3//! [simple types]: https://www.w3schools.com/xml/el_simpletype.asp
4//! [as defined]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
5
6use crate::errors::serialize::DeError;
7use crate::escapei::_escape;
8use crate::se::{Indent, QuoteLevel};
9use serde::ser::{
10 Impossible, Serialize, SerializeSeq, SerializeTuple, SerializeTupleStruct, Serializer,
11};
12use serde::serde_if_integer128;
13use std::borrow::Cow;
14use std::fmt::Write;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum QuoteTarget {
18 /// Escape data for a text content. No additional escape symbols
19 Text,
20 /// Escape data for a double-quoted attribute. `"` always escaped
21 DoubleQAttr,
22 /// Escape data for a single-quoted attribute. `'` always escaped
23 SingleQAttr,
24}
25
26/// Escapes atomic value that could be part of a `xs:list`. All whitespace characters
27/// additionally escaped
28fn escape_item(value: &str, target: QuoteTarget, level: QuoteLevel) -> Cow<str> {
29 use QuoteLevel::*;
30 use QuoteTarget::*;
31
32 match (target, level) {
33 (_, Full) => _escape(value, |ch| match ch {
34 // Spaces used as delimiters of list items, cannot be used in the item
35 b' ' | b'\r' | b'\n' | b'\t' => true,
36 // Required characters to escape
37 b'&' | b'<' | b'>' | b'\'' | b'\"' => true,
38 _ => false,
39 }),
40 //----------------------------------------------------------------------
41 (Text, Partial) => _escape(value, |ch| match ch {
42 // Spaces used as delimiters of list items, cannot be used in the item
43 b' ' | b'\r' | b'\n' | b'\t' => true,
44 // Required characters to escape
45 b'&' | b'<' | b'>' => true,
46 _ => false,
47 }),
48 (Text, Minimal) => _escape(value, |ch| match ch {
49 // Spaces used as delimiters of list items, cannot be used in the item
50 b' ' | b'\r' | b'\n' | b'\t' => true,
51 // Required characters to escape
52 b'&' | b'<' => true,
53 _ => false,
54 }),
55 //----------------------------------------------------------------------
56 (DoubleQAttr, Partial) => _escape(value, |ch| match ch {
57 // Spaces used as delimiters of list items, cannot be used in the item
58 b' ' | b'\r' | b'\n' | b'\t' => true,
59 // Required characters to escape
60 b'&' | b'<' | b'>' => true,
61 // Double quoted attribute should escape quote
62 b'"' => true,
63 _ => false,
64 }),
65 (DoubleQAttr, Minimal) => _escape(value, |ch| match ch {
66 // Spaces used as delimiters of list items, cannot be used in the item
67 b' ' | b'\r' | b'\n' | b'\t' => true,
68 // Required characters to escape
69 b'&' | b'<' => true,
70 // Double quoted attribute should escape quote
71 b'"' => true,
72 _ => false,
73 }),
74 //----------------------------------------------------------------------
75 (SingleQAttr, Partial) => _escape(value, |ch| match ch {
76 // Spaces used as delimiters of list items
77 b' ' | b'\r' | b'\n' | b'\t' => true,
78 // Required characters to escape
79 b'&' | b'<' | b'>' => true,
80 // Single quoted attribute should escape quote
81 b'\'' => true,
82 _ => false,
83 }),
84 (SingleQAttr, Minimal) => _escape(value, |ch| match ch {
85 // Spaces used as delimiters of list items
86 b' ' | b'\r' | b'\n' | b'\t' => true,
87 // Required characters to escape
88 b'&' | b'<' => true,
89 // Single quoted attribute should escape quote
90 b'\'' => true,
91 _ => false,
92 }),
93 }
94}
95
96/// Escapes XSD simple type value
97fn escape_list(value: &str, target: QuoteTarget, level: QuoteLevel) -> Cow<str> {
98 use QuoteLevel::*;
99 use QuoteTarget::*;
100
101 match (target, level) {
102 (_, Full) => _escape(value, |ch| match ch {
103 // Required characters to escape
104 b'&' | b'<' | b'>' | b'\'' | b'\"' => true,
105 _ => false,
106 }),
107 //----------------------------------------------------------------------
108 (Text, Partial) => _escape(value, |ch| match ch {
109 // Required characters to escape
110 b'&' | b'<' | b'>' => true,
111 _ => false,
112 }),
113 (Text, Minimal) => _escape(value, |ch| match ch {
114 // Required characters to escape
115 b'&' | b'<' => true,
116 _ => false,
117 }),
118 //----------------------------------------------------------------------
119 (DoubleQAttr, Partial) => _escape(value, |ch| match ch {
120 // Required characters to escape
121 b'&' | b'<' | b'>' => true,
122 // Double quoted attribute should escape quote
123 b'"' => true,
124 _ => false,
125 }),
126 (DoubleQAttr, Minimal) => _escape(value, |ch| match ch {
127 // Required characters to escape
128 b'&' | b'<' => true,
129 // Double quoted attribute should escape quote
130 b'"' => true,
131 _ => false,
132 }),
133 //----------------------------------------------------------------------
134 (SingleQAttr, Partial) => _escape(value, |ch| match ch {
135 // Required characters to escape
136 b'&' | b'<' | b'>' => true,
137 // Single quoted attribute should escape quote
138 b'\'' => true,
139 _ => false,
140 }),
141 (SingleQAttr, Minimal) => _escape(value, |ch| match ch {
142 // Required characters to escape
143 b'&' | b'<' => true,
144 // Single quoted attribute should escape quote
145 b'\'' => true,
146 _ => false,
147 }),
148 }
149}
150
151////////////////////////////////////////////////////////////////////////////////////////////////////
152
153/// A serializer that handles ordinary [simple type definition][item] with
154/// `{variety} = atomic`, or an ordinary [simple type] definition with
155/// `{variety} = union` whose basic members are all atomic.
156///
157/// This serializer can serialize only primitive types:
158/// - numbers
159/// - booleans
160/// - strings
161/// - units
162/// - options
163/// - unit variants of enums
164///
165/// Identifiers represented as strings and serialized accordingly.
166///
167/// Serialization of all other types returns [`Unsupported`][DeError::Unsupported] error.
168///
169/// [item]: https://www.w3.org/TR/xmlschema11-1/#std-item_type_definition
170/// [simple type]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
171pub struct AtomicSerializer<W: Write> {
172 pub writer: W,
173 pub target: QuoteTarget,
174 /// Defines which XML characters need to be escaped
175 pub level: QuoteLevel,
176}
177
178impl<W: Write> AtomicSerializer<W> {
179 fn write_str(&mut self, value: &str) -> Result<(), DeError> {
180 Ok(self
181 .writer
182 .write_str(&escape_item(value, self.target, self.level))?)
183 }
184}
185
186impl<W: Write> Serializer for AtomicSerializer<W> {
187 type Ok = W;
188 type Error = DeError;
189
190 type SerializeSeq = Impossible<Self::Ok, Self::Error>;
191 type SerializeTuple = Impossible<Self::Ok, Self::Error>;
192 type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
193 type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
194 type SerializeMap = Impossible<Self::Ok, Self::Error>;
195 type SerializeStruct = Impossible<Self::Ok, Self::Error>;
196 type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
197
198 write_primitive!();
199
200 fn serialize_str(mut self, value: &str) -> Result<Self::Ok, Self::Error> {
201 self.write_str(value)?;
202 Ok(self.writer)
203 }
204
205 /// We cannot store anything, so the absence of a unit and presence of it
206 /// does not differ, so serialization of unit returns `Err(Unsupported)`
207 fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
208 Err(DeError::Unsupported(
209 "unit type `()` cannot be serialized as an `xs:list` item".into(),
210 ))
211 }
212
213 /// We cannot store anything, so the absence of a unit and presence of it
214 /// does not differ, so serialization of unit returns `Err(Unsupported)`
215 fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
216 Err(DeError::Unsupported(
217 format!(
218 "unit struct `{}` cannot be serialized as an `xs:list` item",
219 name
220 )
221 .into(),
222 ))
223 }
224
225 /// We cannot store both a variant discriminant and a variant value,
226 /// so serialization of enum newtype variant returns `Err(Unsupported)`
227 fn serialize_newtype_variant<T: ?Sized + Serialize>(
228 self,
229 name: &'static str,
230 _variant_index: u32,
231 variant: &'static str,
232 _value: &T,
233 ) -> Result<Self::Ok, DeError> {
234 Err(DeError::Unsupported(
235 format!(
236 "enum newtype variant `{}::{}` cannot be serialized as an `xs:list` item",
237 name, variant
238 )
239 .into(),
240 ))
241 }
242
243 fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
244 Err(DeError::Unsupported(
245 "sequence cannot be serialized as an `xs:list` item".into(),
246 ))
247 }
248
249 fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
250 Err(DeError::Unsupported(
251 "tuple cannot be serialized as an `xs:list` item".into(),
252 ))
253 }
254
255 fn serialize_tuple_struct(
256 self,
257 name: &'static str,
258 _len: usize,
259 ) -> Result<Self::SerializeTupleStruct, Self::Error> {
260 Err(DeError::Unsupported(
261 format!(
262 "tuple struct `{}` cannot be serialized as an `xs:list` item",
263 name
264 )
265 .into(),
266 ))
267 }
268
269 fn serialize_tuple_variant(
270 self,
271 name: &'static str,
272 _variant_index: u32,
273 variant: &'static str,
274 _len: usize,
275 ) -> Result<Self::SerializeTupleVariant, Self::Error> {
276 Err(DeError::Unsupported(
277 format!(
278 "enum tuple variant `{}::{}` cannot be serialized as an `xs:list` item",
279 name, variant
280 )
281 .into(),
282 ))
283 }
284
285 fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
286 Err(DeError::Unsupported(
287 "map cannot be serialized as an `xs:list` item".into(),
288 ))
289 }
290
291 fn serialize_struct(
292 self,
293 name: &'static str,
294 _len: usize,
295 ) -> Result<Self::SerializeStruct, Self::Error> {
296 Err(DeError::Unsupported(
297 format!(
298 "struct `{}` cannot be serialized as an `xs:list` item",
299 name
300 )
301 .into(),
302 ))
303 }
304
305 fn serialize_struct_variant(
306 self,
307 name: &'static str,
308 _variant_index: u32,
309 variant: &'static str,
310 _len: usize,
311 ) -> Result<Self::SerializeStructVariant, Self::Error> {
312 Err(DeError::Unsupported(
313 format!(
314 "enum struct variant `{}::{}` cannot be serialized as an `xs:list` item",
315 name, variant
316 )
317 .into(),
318 ))
319 }
320}
321
322////////////////////////////////////////////////////////////////////////////////////////////////////
323
324/// A serializer for a values representing XSD [simple types], which used in:
325/// - attribute values (`<... ...="value" ...>`)
326/// - text content (`<...>text</...>`)
327/// - CDATA content (`<...><![CDATA[cdata]]></...>`)
328///
329/// [simple types]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
330pub struct SimpleTypeSerializer<'i, W: Write> {
331 /// Writer to which this serializer writes content
332 pub writer: W,
333 /// Target for which element is serializing. Affects additional characters to escape.
334 pub target: QuoteTarget,
335 /// Defines which XML characters need to be escaped
336 pub level: QuoteLevel,
337 /// Indent that should be written before the content if content is not an empty string
338 pub(crate) indent: Indent<'i>,
339}
340
341impl<'i, W: Write> SimpleTypeSerializer<'i, W> {
342 fn write_str(&mut self, value: &str) -> Result<(), DeError> {
343 self.indent.write_indent(&mut self.writer)?;
344 Ok(self
345 .writer
346 .write_str(&escape_list(value, self.target, self.level))?)
347 }
348}
349
350impl<'i, W: Write> Serializer for SimpleTypeSerializer<'i, W> {
351 type Ok = W;
352 type Error = DeError;
353
354 type SerializeSeq = SimpleSeq<'i, W>;
355 type SerializeTuple = SimpleSeq<'i, W>;
356 type SerializeTupleStruct = SimpleSeq<'i, W>;
357 type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
358 type SerializeMap = Impossible<Self::Ok, Self::Error>;
359 type SerializeStruct = Impossible<Self::Ok, Self::Error>;
360 type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
361
362 write_primitive!();
363
364 fn serialize_str(mut self, value: &str) -> Result<Self::Ok, Self::Error> {
365 if value.is_empty() {
366 self.indent = Indent::None;
367 }
368 self.write_str(value)?;
369 Ok(self.writer)
370 }
371
372 /// Does not write anything
373 fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
374 Ok(self.writer)
375 }
376
377 /// Does not write anything
378 fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
379 Ok(self.writer)
380 }
381
382 /// We cannot store both a variant discriminant and a variant value,
383 /// so serialization of enum newtype variant returns `Err(Unsupported)`
384 fn serialize_newtype_variant<T: ?Sized + Serialize>(
385 self,
386 name: &'static str,
387 _variant_index: u32,
388 variant: &'static str,
389 _value: &T,
390 ) -> Result<Self::Ok, DeError> {
391 Err(DeError::Unsupported(
392 format!("enum newtype variant `{}::{}` cannot be serialized as an attribute or text content value", name, variant).into(),
393 ))
394 }
395
396 #[inline]
397 fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
398 Ok(SimpleSeq {
399 writer: self.writer,
400 target: self.target,
401 level: self.level,
402 first: true,
403 indent: self.indent,
404 })
405 }
406
407 #[inline]
408 fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
409 self.serialize_seq(None)
410 }
411
412 #[inline]
413 fn serialize_tuple_struct(
414 self,
415 _name: &'static str,
416 _len: usize,
417 ) -> Result<Self::SerializeTupleStruct, Self::Error> {
418 self.serialize_seq(None)
419 }
420
421 fn serialize_tuple_variant(
422 self,
423 name: &'static str,
424 _variant_index: u32,
425 variant: &'static str,
426 _len: usize,
427 ) -> Result<Self::SerializeTupleVariant, Self::Error> {
428 Err(DeError::Unsupported(
429 format!("enum tuple variant `{}::{}` cannot be serialized as an attribute or text content value", name, variant).into(),
430 ))
431 }
432
433 fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
434 Err(DeError::Unsupported(
435 "map cannot be serialized as an attribute or text content value".into(),
436 ))
437 }
438
439 fn serialize_struct(
440 self,
441 name: &'static str,
442 _len: usize,
443 ) -> Result<Self::SerializeStruct, Self::Error> {
444 Err(DeError::Unsupported(
445 format!(
446 "struct `{}` cannot be serialized as an attribute or text content value",
447 name
448 )
449 .into(),
450 ))
451 }
452
453 fn serialize_struct_variant(
454 self,
455 name: &'static str,
456 _variant_index: u32,
457 variant: &'static str,
458 _len: usize,
459 ) -> Result<Self::SerializeStructVariant, Self::Error> {
460 Err(DeError::Unsupported(
461 format!("enum struct variant `{}::{}` cannot be serialized as an attribute or text content value", name, variant).into(),
462 ))
463 }
464}
465
466/// Serializer for a sequence of atomic values delimited by space
467pub struct SimpleSeq<'i, W: Write> {
468 writer: W,
469 target: QuoteTarget,
470 level: QuoteLevel,
471 /// If `true`, nothing was written yet
472 first: bool,
473 /// Indent that should be written before the content if content is not an empty string
474 indent: Indent<'i>,
475}
476
477impl<'i, W: Write> SerializeSeq for SimpleSeq<'i, W> {
478 type Ok = W;
479 type Error = DeError;
480
481 fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
482 where
483 T: ?Sized + Serialize,
484 {
485 // Write indent for the first element and delimiter for others
486 //FIXME: sequence with only empty strings will be serialized as indent only + delimiters
487 if self.first {
488 self.indent.write_indent(&mut self.writer)?;
489 } else {
490 self.writer.write_char(' ')?;
491 }
492 self.first = false;
493 value.serialize(AtomicSerializer {
494 writer: &mut self.writer,
495 target: self.target,
496 level: self.level,
497 })?;
498 Ok(())
499 }
500
501 #[inline]
502 fn end(self) -> Result<Self::Ok, Self::Error> {
503 Ok(self.writer)
504 }
505}
506
507impl<'i, W: Write> SerializeTuple for SimpleSeq<'i, W> {
508 type Ok = W;
509 type Error = DeError;
510
511 #[inline]
512 fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
513 where
514 T: ?Sized + Serialize,
515 {
516 <Self as SerializeSeq>::serialize_element(self, value)
517 }
518
519 #[inline]
520 fn end(self) -> Result<Self::Ok, Self::Error> {
521 <Self as SerializeSeq>::end(self)
522 }
523}
524
525impl<'i, W: Write> SerializeTupleStruct for SimpleSeq<'i, W> {
526 type Ok = W;
527 type Error = DeError;
528
529 #[inline]
530 fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
531 where
532 T: ?Sized + Serialize,
533 {
534 <Self as SerializeSeq>::serialize_element(self, value)
535 }
536
537 #[inline]
538 fn end(self) -> Result<Self::Ok, Self::Error> {
539 <Self as SerializeSeq>::end(self)
540 }
541}
542
543////////////////////////////////////////////////////////////////////////////////////////////////////
544
545#[cfg(test)]
546mod tests {
547 use super::*;
548 use crate::utils::Bytes;
549 use serde::Serialize;
550 use std::collections::BTreeMap;
551
552 #[derive(Debug, Serialize, PartialEq)]
553 struct Unit;
554
555 #[derive(Debug, Serialize, PartialEq)]
556 struct Newtype(usize);
557
558 #[derive(Debug, Serialize, PartialEq)]
559 struct Tuple(&'static str, usize);
560
561 #[derive(Debug, Serialize, PartialEq)]
562 struct Struct {
563 key: &'static str,
564 val: usize,
565 }
566
567 #[derive(Debug, Serialize, PartialEq)]
568 enum Enum {
569 Unit,
570 #[serde(rename = "<\"&'>")]
571 UnitEscaped,
572 Newtype(usize),
573 Tuple(&'static str, usize),
574 Struct {
575 key: &'static str,
576 val: usize,
577 },
578 }
579
580 mod escape_item {
581 use super::*;
582
583 mod full {
584 use super::*;
585 use pretty_assertions::assert_eq;
586
587 #[test]
588 fn text() {
589 assert_eq!(
590 escape_item("text<\"'&> \t\n\rtext", QuoteTarget::Text, QuoteLevel::Full),
591 "text&lt;&quot;&apos;&amp;&gt;&#32;&#9;&#10;&#13;text"
592 );
593 }
594
595 #[test]
596 fn double_quote_attr() {
597 assert_eq!(
598 escape_item(
599 "text<\"'&> \t\n\rtext",
600 QuoteTarget::DoubleQAttr,
601 QuoteLevel::Full
602 ),
603 "text&lt;&quot;&apos;&amp;&gt;&#32;&#9;&#10;&#13;text"
604 );
605 }
606
607 #[test]
608 fn single_quote_attr() {
609 assert_eq!(
610 escape_item(
611 "text<\"'&> \t\n\rtext",
612 QuoteTarget::SingleQAttr,
613 QuoteLevel::Full
614 ),
615 "text&lt;&quot;&apos;&amp;&gt;&#32;&#9;&#10;&#13;text"
616 );
617 }
618 }
619
620 mod partial {
621 use super::*;
622 use pretty_assertions::assert_eq;
623
624 #[test]
625 fn text() {
626 assert_eq!(
627 escape_item(
628 "text<\"'&> \t\n\rtext",
629 QuoteTarget::Text,
630 QuoteLevel::Partial
631 ),
632 "text&lt;\"'&amp;&gt;&#32;&#9;&#10;&#13;text"
633 );
634 }
635
636 #[test]
637 fn double_quote_attr() {
638 assert_eq!(
639 escape_item(
640 "text<\"'&> \t\n\rtext",
641 QuoteTarget::DoubleQAttr,
642 QuoteLevel::Partial
643 ),
644 "text&lt;&quot;'&amp;&gt;&#32;&#9;&#10;&#13;text"
645 );
646 }
647
648 #[test]
649 fn single_quote_attr() {
650 assert_eq!(
651 escape_item(
652 "text<\"'&> \t\n\rtext",
653 QuoteTarget::SingleQAttr,
654 QuoteLevel::Partial
655 ),
656 "text&lt;\"&apos;&amp;&gt;&#32;&#9;&#10;&#13;text"
657 );
658 }
659 }
660
661 mod minimal {
662 use super::*;
663 use pretty_assertions::assert_eq;
664
665 #[test]
666 fn text() {
667 assert_eq!(
668 escape_item(
669 "text<\"'&> \t\n\rtext",
670 QuoteTarget::Text,
671 QuoteLevel::Minimal
672 ),
673 "text&lt;\"'&amp;>&#32;&#9;&#10;&#13;text"
674 );
675 }
676
677 #[test]
678 fn double_quote_attr() {
679 assert_eq!(
680 escape_item(
681 "text<\"'&> \t\n\rtext",
682 QuoteTarget::DoubleQAttr,
683 QuoteLevel::Minimal
684 ),
685 "text&lt;&quot;'&amp;>&#32;&#9;&#10;&#13;text"
686 );
687 }
688
689 #[test]
690 fn single_quote_attr() {
691 assert_eq!(
692 escape_item(
693 "text<\"'&> \t\n\rtext",
694 QuoteTarget::SingleQAttr,
695 QuoteLevel::Minimal
696 ),
697 "text&lt;\"&apos;&amp;>&#32;&#9;&#10;&#13;text"
698 );
699 }
700 }
701 }
702
703 mod escape_list {
704 use super::*;
705
706 mod full {
707 use super::*;
708 use pretty_assertions::assert_eq;
709
710 #[test]
711 fn text() {
712 assert_eq!(
713 escape_list("text<\"'&> \t\n\rtext", QuoteTarget::Text, QuoteLevel::Full),
714 "text&lt;&quot;&apos;&amp;&gt; \t\n\rtext"
715 );
716 }
717
718 #[test]
719 fn double_quote_attr() {
720 assert_eq!(
721 escape_list(
722 "text<\"'&> \t\n\rtext",
723 QuoteTarget::DoubleQAttr,
724 QuoteLevel::Full
725 ),
726 "text&lt;&quot;&apos;&amp;&gt; \t\n\rtext"
727 );
728 }
729
730 #[test]
731 fn single_quote_attr() {
732 assert_eq!(
733 escape_list(
734 "text<\"'&> \t\n\rtext",
735 QuoteTarget::SingleQAttr,
736 QuoteLevel::Full
737 ),
738 "text&lt;&quot;&apos;&amp;&gt; \t\n\rtext"
739 );
740 }
741 }
742
743 mod partial {
744 use super::*;
745 use pretty_assertions::assert_eq;
746
747 #[test]
748 fn text() {
749 assert_eq!(
750 escape_list(
751 "text<\"'&> \t\n\rtext",
752 QuoteTarget::Text,
753 QuoteLevel::Partial
754 ),
755 "text&lt;\"'&amp;&gt; \t\n\rtext"
756 );
757 }
758
759 #[test]
760 fn double_quote_attr() {
761 assert_eq!(
762 escape_list(
763 "text<\"'&> \t\n\rtext",
764 QuoteTarget::DoubleQAttr,
765 QuoteLevel::Partial
766 ),
767 "text&lt;&quot;'&amp;&gt; \t\n\rtext"
768 );
769 }
770
771 #[test]
772 fn single_quote_attr() {
773 assert_eq!(
774 escape_list(
775 "text<\"'&> \t\n\rtext",
776 QuoteTarget::SingleQAttr,
777 QuoteLevel::Partial
778 ),
779 "text&lt;\"&apos;&amp;&gt; \t\n\rtext"
780 );
781 }
782 }
783
784 mod minimal {
785 use super::*;
786 use pretty_assertions::assert_eq;
787
788 #[test]
789 fn text() {
790 assert_eq!(
791 escape_list(
792 "text<\"'&> \t\n\rtext",
793 QuoteTarget::Text,
794 QuoteLevel::Minimal
795 ),
796 "text&lt;\"'&amp;> \t\n\rtext"
797 );
798 }
799
800 #[test]
801 fn double_quote_attr() {
802 assert_eq!(
803 escape_list(
804 "text<\"'&> \t\n\rtext",
805 QuoteTarget::DoubleQAttr,
806 QuoteLevel::Minimal
807 ),
808 "text&lt;&quot;'&amp;> \t\n\rtext"
809 );
810 }
811
812 #[test]
813 fn single_quote_attr() {
814 assert_eq!(
815 escape_list(
816 "text<\"'&> \t\n\rtext",
817 QuoteTarget::SingleQAttr,
818 QuoteLevel::Minimal
819 ),
820 "text&lt;\"&apos;&amp;> \t\n\rtext"
821 );
822 }
823 }
824 }
825
826 /// Tests for serialize atomic and union values, as defined in XSD specification
827 mod atomic {
828 use super::*;
829 use pretty_assertions::assert_eq;
830
831 /// Checks that given `$data` successfully serialized as `$expected`
832 macro_rules! serialize_as {
833 ($name:ident: $data:expr => $expected:literal) => {
834 #[test]
835 fn $name() {
836 let ser = AtomicSerializer {
837 writer: String::new(),
838 target: QuoteTarget::Text,
839 level: QuoteLevel::Full,
840 };
841
842 let buffer = $data.serialize(ser).unwrap();
843 assert_eq!(buffer, $expected);
844 }
845 };
846 }
847
848 /// Checks that attempt to serialize given `$data` results to a
849 /// serialization error `$kind` with `$reason`
850 macro_rules! err {
851 ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
852 #[test]
853 fn $name() {
854 let mut buffer = String::new();
855 let ser = AtomicSerializer {
856 writer: &mut buffer,
857 target: QuoteTarget::Text,
858 level: QuoteLevel::Full,
859 };
860
861 match $data.serialize(ser).unwrap_err() {
862 DeError::$kind(e) => assert_eq!(e, $reason),
863 e => panic!(
864 "Expected `{}({})`, found `{:?}`",
865 stringify!($kind),
866 $reason,
867 e
868 ),
869 }
870 assert_eq!(buffer, "");
871 }
872 };
873 }
874
875 serialize_as!(false_: false => "false");
876 serialize_as!(true_: true => "true");
877
878 serialize_as!(i8_: -42i8 => "-42");
879 serialize_as!(i16_: -4200i16 => "-4200");
880 serialize_as!(i32_: -42000000i32 => "-42000000");
881 serialize_as!(i64_: -42000000000000i64 => "-42000000000000");
882 serialize_as!(isize_: -42000000000000isize => "-42000000000000");
883
884 serialize_as!(u8_: 42u8 => "42");
885 serialize_as!(u16_: 4200u16 => "4200");
886 serialize_as!(u32_: 42000000u32 => "42000000");
887 serialize_as!(u64_: 42000000000000u64 => "42000000000000");
888 serialize_as!(usize_: 42000000000000usize => "42000000000000");
889
890 serde_if_integer128! {
891 serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
892 serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000");
893 }
894
895 serialize_as!(f32_: 4.2f32 => "4.2");
896 serialize_as!(f64_: 4.2f64 => "4.2");
897
898 serialize_as!(char_non_escaped: 'h' => "h");
899 serialize_as!(char_lt: '<' => "&lt;");
900 serialize_as!(char_gt: '>' => "&gt;");
901 serialize_as!(char_amp: '&' => "&amp;");
902 serialize_as!(char_apos: '\'' => "&apos;");
903 serialize_as!(char_quot: '"' => "&quot;");
904
905 serialize_as!(str_non_escaped: "non-escaped-string" => "non-escaped-string");
906 serialize_as!(str_escaped: "<\"escaped & string'>" => "&lt;&quot;escaped&#32;&amp;&#32;string&apos;&gt;");
907
908 err!(bytes: Bytes(b"<\"escaped & bytes'>")
909 => Unsupported("`serialize_bytes` not supported yet"));
910
911 serialize_as!(option_none: Option::<&str>::None => "");
912 serialize_as!(option_some: Some("non-escaped-string") => "non-escaped-string");
913
914 err!(unit: ()
915 => Unsupported("unit type `()` cannot be serialized as an `xs:list` item"));
916 err!(unit_struct: Unit
917 => Unsupported("unit struct `Unit` cannot be serialized as an `xs:list` item"));
918
919 serialize_as!(enum_unit: Enum::Unit => "Unit");
920 serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "&lt;&quot;&amp;&apos;&gt;");
921
922 serialize_as!(newtype: Newtype(42) => "42");
923 err!(enum_newtype: Enum::Newtype(42)
924 => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an `xs:list` item"));
925
926 err!(seq: vec![1, 2, 3]
927 => Unsupported("sequence cannot be serialized as an `xs:list` item"));
928 err!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize)
929 => Unsupported("tuple cannot be serialized as an `xs:list` item"));
930 err!(tuple_struct: Tuple("first", 42)
931 => Unsupported("tuple struct `Tuple` cannot be serialized as an `xs:list` item"));
932 err!(enum_tuple: Enum::Tuple("first", 42)
933 => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an `xs:list` item"));
934
935 err!(map: BTreeMap::from([(1, 2), (3, 4)])
936 => Unsupported("map cannot be serialized as an `xs:list` item"));
937 err!(struct_: Struct { key: "answer", val: 42 }
938 => Unsupported("struct `Struct` cannot be serialized as an `xs:list` item"));
939 err!(enum_struct: Enum::Struct { key: "answer", val: 42 }
940 => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an `xs:list` item"));
941 }
942
943 mod simple_type {
944 use super::*;
945 use pretty_assertions::assert_eq;
946
947 /// Checks that given `$data` successfully serialized as `$expected`
948 macro_rules! serialize_as {
949 ($name:ident: $data:expr => $expected:literal) => {
950 #[test]
951 fn $name() {
952 let ser = SimpleTypeSerializer {
953 writer: String::new(),
954 target: QuoteTarget::Text,
955 level: QuoteLevel::Full,
956 indent: Indent::None,
957 };
958
959 let buffer = $data.serialize(ser).unwrap();
960 assert_eq!(buffer, $expected);
961 }
962 };
963 }
964
965 /// Checks that attempt to serialize given `$data` results to a
966 /// serialization error `$kind` with `$reason`
967 macro_rules! err {
968 ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
969 #[test]
970 fn $name() {
971 let mut buffer = String::new();
972 let ser = SimpleTypeSerializer {
973 writer: &mut buffer,
974 target: QuoteTarget::Text,
975 level: QuoteLevel::Full,
976 indent: Indent::None,
977 };
978
979 match $data.serialize(ser).unwrap_err() {
980 DeError::$kind(e) => assert_eq!(e, $reason),
981 e => panic!(
982 "Expected `{}({})`, found `{:?}`",
983 stringify!($kind),
984 $reason,
985 e
986 ),
987 }
988 assert_eq!(buffer, "");
989 }
990 };
991 }
992
993 serialize_as!(false_: false => "false");
994 serialize_as!(true_: true => "true");
995
996 serialize_as!(i8_: -42i8 => "-42");
997 serialize_as!(i16_: -4200i16 => "-4200");
998 serialize_as!(i32_: -42000000i32 => "-42000000");
999 serialize_as!(i64_: -42000000000000i64 => "-42000000000000");
1000 serialize_as!(isize_: -42000000000000isize => "-42000000000000");
1001
1002 serialize_as!(u8_: 42u8 => "42");
1003 serialize_as!(u16_: 4200u16 => "4200");
1004 serialize_as!(u32_: 42000000u32 => "42000000");
1005 serialize_as!(u64_: 42000000000000u64 => "42000000000000");
1006 serialize_as!(usize_: 42000000000000usize => "42000000000000");
1007
1008 serde_if_integer128! {
1009 serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
1010 serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000");
1011 }
1012
1013 serialize_as!(f32_: 4.2f32 => "4.2");
1014 serialize_as!(f64_: 4.2f64 => "4.2");
1015
1016 serialize_as!(char_non_escaped: 'h' => "h");
1017 serialize_as!(char_lt: '<' => "&lt;");
1018 serialize_as!(char_gt: '>' => "&gt;");
1019 serialize_as!(char_amp: '&' => "&amp;");
1020 serialize_as!(char_apos: '\'' => "&apos;");
1021 serialize_as!(char_quot: '"' => "&quot;");
1022
1023 serialize_as!(str_non_escaped: "non-escaped string" => "non-escaped string");
1024 serialize_as!(str_escaped: "<\"escaped & string'>" => "&lt;&quot;escaped &amp; string&apos;&gt;");
1025
1026 err!(bytes: Bytes(b"<\"escaped & bytes'>")
1027 => Unsupported("`serialize_bytes` not supported yet"));
1028
1029 serialize_as!(option_none: Option::<&str>::None => "");
1030 serialize_as!(option_some: Some("non-escaped string") => "non-escaped string");
1031
1032 serialize_as!(unit: () => "");
1033 serialize_as!(unit_struct: Unit => "");
1034
1035 serialize_as!(enum_unit: Enum::Unit => "Unit");
1036 serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "&lt;&quot;&amp;&apos;&gt;");
1037
1038 serialize_as!(newtype: Newtype(42) => "42");
1039 err!(enum_newtype: Enum::Newtype(42)
1040 => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an attribute or text content value"));
1041
1042 serialize_as!(seq: vec![1, 2, 3] => "1 2 3");
1043 serialize_as!(seq_empty: Vec::<usize>::new() => "");
1044 serialize_as!(seq_with_1_empty_str: vec![""] => "");
1045 serialize_as!(seq_with_2_empty_strs: vec!["", ""] => " ");
1046 serialize_as!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize)
1047 => "&lt;&quot;&amp;&apos;&gt; with&#9;&#10;&#13;&#32;spaces 3");
1048 serialize_as!(tuple_struct: Tuple("first", 42) => "first 42");
1049 err!(enum_tuple: Enum::Tuple("first", 42)
1050 => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an attribute or text content value"));
1051
1052 err!(map: BTreeMap::from([(1, 2), (3, 4)])
1053 => Unsupported("map cannot be serialized as an attribute or text content value"));
1054 err!(struct_: Struct { key: "answer", val: 42 }
1055 => Unsupported("struct `Struct` cannot be serialized as an attribute or text content value"));
1056 err!(enum_struct: Enum::Struct { key: "answer", val: 42 }
1057 => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an attribute or text content value"));
1058 }
1059}
1060