Skip to main content

quick_xml/se/
simple_type.rs

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::escape::escape_char;
7use crate::se::{QuoteLevel, SeError};
8use crate::utils::CDataIterator;
9use serde::ser::{
10    Impossible, Serialize, SerializeSeq, SerializeTuple, SerializeTupleStruct,
11    SerializeTupleVariant, Serializer,
12};
13use std::fmt::{self, Write};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum QuoteTarget {
17    /// Escape data for a text content. No additional escape symbols
18    Text,
19    /// Escape data for a double-quoted attribute. `"` always escaped
20    DoubleQAttr,
21    /// Escape data for a single-quoted attribute. `'` always escaped
22    SingleQAttr,
23    /// Escape data for a CDATA content. No escaping for `&` and `>`, but split
24    /// content on `]]>` and make several CDATA sections
25    CData,
26}
27
28fn escape_into<W, F>(mut writer: W, value: &str, escape_chars: F) -> fmt::Result
29where
30    W: Write,
31    F: Fn(u8) -> bool,
32{
33    let bytes = value.as_bytes();
34    let mut iter = bytes.iter();
35    let mut pos = 0;
36    while let Some(i) = iter.position(|&b| escape_chars(b)) {
37        let new_pos = pos + i;
38        escape_char(&mut writer, value, pos, new_pos)?;
39        pos = new_pos + 1;
40    }
41
42    if let Some(raw) = value.get(pos..) {
43        writer.write_str(raw)?;
44    }
45    Ok(())
46}
47
48/// Escapes atomic value that could be part of a `xs:list`. All whitespace characters
49/// additionally escaped
50fn escape_item<W>(mut writer: W, value: &str, target: QuoteTarget, level: QuoteLevel) -> fmt::Result
51where
52    W: Write,
53{
54    use QuoteLevel::*;
55    use QuoteTarget::*;
56
57    match (target, level) {
58        (CData, _) => {
59            let mut it = CDataIterator::new(value);
60            if let Some(part) = it.next() {
61                writer.write_str(part)?;
62            }
63            for part in it {
64                writer.write_str("]]><![CDATA[")?;
65                writer.write_str(part)?;
66            }
67            Ok(())
68        }
69        (_, Full) => escape_into(writer, value, |ch| match ch {
70            // Spaces used as delimiters of list items, cannot be used in the item
71            b' ' | b'\r' | b'\n' | b'\t' => true,
72            // Required characters to escape
73            b'&' | b'<' | b'>' | b'\'' | b'\"' => true,
74            _ => false,
75        }),
76        //----------------------------------------------------------------------
77        (Text, Partial) => escape_into(writer, value, |ch| match ch {
78            // Spaces used as delimiters of list items, cannot be used in the item
79            b' ' | b'\r' | b'\n' | b'\t' => true,
80            // Required characters to escape
81            b'&' | b'<' | b'>' => true,
82            _ => false,
83        }),
84        (Text, Minimal) => escape_into(writer, value, |ch| match ch {
85            // Spaces used as delimiters of list items, cannot be used in the item
86            b' ' | b'\r' | b'\n' | b'\t' => true,
87            // Required characters to escape
88            b'&' | b'<' => true,
89            _ => false,
90        }),
91        //----------------------------------------------------------------------
92        (DoubleQAttr, Partial) => escape_into(writer, value, |ch| match ch {
93            // Spaces used as delimiters of list items, cannot be used in the item
94            b' ' | b'\r' | b'\n' | b'\t' => true,
95            // Required characters to escape
96            b'&' | b'<' | b'>' => true,
97            // Double quoted attribute should escape quote
98            b'"' => true,
99            _ => false,
100        }),
101        (DoubleQAttr, Minimal) => escape_into(writer, value, |ch| match ch {
102            // Spaces used as delimiters of list items, cannot be used in the item
103            b' ' | b'\r' | b'\n' | b'\t' => true,
104            // Required characters to escape
105            b'&' | b'<' => true,
106            // Double quoted attribute should escape quote
107            b'"' => true,
108            _ => false,
109        }),
110        //----------------------------------------------------------------------
111        (SingleQAttr, Partial) => escape_into(writer, value, |ch| match ch {
112            // Spaces used as delimiters of list items
113            b' ' | b'\r' | b'\n' | b'\t' => true,
114            // Required characters to escape
115            b'&' | b'<' | b'>' => true,
116            // Single quoted attribute should escape quote
117            b'\'' => true,
118            _ => false,
119        }),
120        (SingleQAttr, Minimal) => escape_into(writer, value, |ch| match ch {
121            // Spaces used as delimiters of list items
122            b' ' | b'\r' | b'\n' | b'\t' => true,
123            // Required characters to escape
124            b'&' | b'<' => true,
125            // Single quoted attribute should escape quote
126            b'\'' => true,
127            _ => false,
128        }),
129    }
130}
131
132/// Escapes XSD simple type value
133///
134/// Escaping \r (and \n, \t in attributes) as character references is not
135/// required by the XML specification, but is necessary for lossless round-trip
136/// preservation because XML parsers normalize these characters on input
137/// (Section 2.11, Section 3.3.3). Character references bypass normalization.
138/// This matches the behavior of libxml2.
139fn escape_list<W>(mut writer: W, value: &str, target: QuoteTarget, level: QuoteLevel) -> fmt::Result
140where
141    W: Write,
142{
143    use QuoteLevel::*;
144    use QuoteTarget::*;
145
146    match (target, level) {
147        (CData, _) => {
148            for part in CDataIterator::new(value) {
149                writer.write_str("<![CDATA[")?;
150                writer.write_str(part)?;
151                writer.write_str("]]>")?;
152            }
153            Ok(())
154        }
155        (Text, Full) => escape_into(writer, value, |ch| match ch {
156            // XML EOL normalization (Section 2.11) would convert \r to \n
157            b'\r' => true,
158            // Required characters to escape
159            b'&' | b'<' | b'>' | b'\'' | b'\"' => true,
160            _ => false,
161        }),
162        (Text, Partial) => escape_into(writer, value, |ch| match ch {
163            // XML EOL normalization (Section 2.11) would convert \r to \n
164            b'\r' => true,
165            // Required characters to escape
166            b'&' | b'<' | b'>' => true,
167            _ => false,
168        }),
169        (Text, Minimal) => escape_into(writer, value, |ch| match ch {
170            // XML EOL normalization (Section 2.11) would convert \r to \n
171            b'\r' => true,
172            // Required characters to escape
173            b'&' | b'<' => true,
174            _ => false,
175        }),
176        //----------------------------------------------------------------------
177        (_, Full) => escape_into(writer, value, |ch| match ch {
178            // XML EOL normalization (Section 2.11) and attribute-value normalization
179            // (Section 3.3.3) would convert these to \n and space respectively
180            b'\r' | b'\n' | b'\t' => true,
181            // Required characters to escape
182            b'&' | b'<' | b'>' | b'\'' | b'\"' => true,
183            _ => false,
184        }),
185        //----------------------------------------------------------------------
186        (DoubleQAttr, Partial) => escape_into(writer, value, |ch| match ch {
187            // XML EOL / attribute-value normalization (Section 2.11, Section 3.3.3)
188            b'\r' | b'\n' | b'\t' => true,
189            // Required characters to escape
190            b'&' | b'<' | b'>' => true,
191            // Double quoted attribute should escape quote
192            b'"' => true,
193            _ => false,
194        }),
195        (DoubleQAttr, Minimal) => escape_into(writer, value, |ch| match ch {
196            // XML EOL / attribute-value normalization (Section 2.11, Section 3.3.3)
197            b'\r' | b'\n' | b'\t' => true,
198            // Required characters to escape
199            b'&' | b'<' => true,
200            // Double quoted attribute should escape quote
201            b'"' => true,
202            _ => false,
203        }),
204        //----------------------------------------------------------------------
205        (SingleQAttr, Partial) => escape_into(writer, value, |ch| match ch {
206            // XML EOL / attribute-value normalization (Section 2.11, Section 3.3.3)
207            b'\r' | b'\n' | b'\t' => true,
208            // Required characters to escape
209            b'&' | b'<' | b'>' => true,
210            // Single quoted attribute should escape quote
211            b'\'' => true,
212            _ => false,
213        }),
214        (SingleQAttr, Minimal) => escape_into(writer, value, |ch| match ch {
215            // XML EOL / attribute-value normalization (Section 2.11, Section 3.3.3)
216            b'\r' | b'\n' | b'\t' => true,
217            // Required characters to escape
218            b'&' | b'<' => true,
219            // Single quoted attribute should escape quote
220            b'\'' => true,
221            _ => false,
222        }),
223    }
224}
225
226////////////////////////////////////////////////////////////////////////////////////////////////////
227
228macro_rules! write_atomic {
229    ($method:ident ( $ty:ty )) => {
230        fn $method(mut self, value: $ty) -> Result<Self::Ok, Self::Error> {
231            self.write_fmt(format_args!("{}", value))?;
232            Ok(true)
233        }
234    };
235}
236
237/// A serializer that handles ordinary [simple type definition][item] with
238/// `{variety} = atomic`, or an ordinary [simple type] definition with
239/// `{variety} = union` whose basic members are all atomic.
240///
241/// This serializer can serialize only primitive types:
242/// - numbers
243/// - booleans
244/// - strings
245/// - units
246/// - options
247/// - unit variants of enums
248///
249/// Identifiers represented as strings and serialized accordingly.
250///
251/// Serialization of all other types returns [`Unsupported`][SeError::Unsupported] error.
252///
253/// This serializer returns `true` if something was written and `false` otherwise.
254///
255/// [item]: https://www.w3.org/TR/xmlschema11-1/#std-item_type_definition
256/// [simple type]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
257pub struct AtomicSerializer<W: Write> {
258    pub writer: W,
259    pub target: QuoteTarget,
260    /// Defines which XML characters need to be escaped
261    pub level: QuoteLevel,
262    /// When `true` an `xs:list` delimiter (a space) should be written
263    pub(crate) write_delimiter: bool,
264}
265
266impl<W: Write> AtomicSerializer<W> {
267    fn write_delimiter(&mut self) -> fmt::Result {
268        if self.write_delimiter {
269            // TODO: Customization point -- possible non-XML compatible extension to specify delimiter char
270            return self.writer.write_char(' ');
271        }
272        Ok(())
273    }
274    fn write_str(&mut self, value: &str) -> Result<(), SeError> {
275        self.write_delimiter()?;
276        Ok(self.writer.write_str(value)?)
277    }
278    fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), SeError> {
279        self.write_delimiter()?;
280        Ok(self.writer.write_fmt(args)?)
281    }
282}
283
284impl<W: Write> Serializer for AtomicSerializer<W> {
285    type Ok = bool;
286    type Error = SeError;
287
288    type SerializeSeq = Impossible<Self::Ok, Self::Error>;
289    type SerializeTuple = Impossible<Self::Ok, Self::Error>;
290    type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
291    type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
292    type SerializeMap = Impossible<Self::Ok, Self::Error>;
293    type SerializeStruct = Impossible<Self::Ok, Self::Error>;
294    type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
295
296    fn serialize_bool(mut self, value: bool) -> Result<Self::Ok, Self::Error> {
297        self.write_str(if value { "true" } else { "false" })?;
298        Ok(true)
299    }
300
301    write_atomic!(serialize_i8(i8));
302    write_atomic!(serialize_i16(i16));
303    write_atomic!(serialize_i32(i32));
304    write_atomic!(serialize_i64(i64));
305
306    write_atomic!(serialize_u8(u8));
307    write_atomic!(serialize_u16(u16));
308    write_atomic!(serialize_u32(u32));
309    write_atomic!(serialize_u64(u64));
310
311    write_atomic!(serialize_i128(i128));
312    write_atomic!(serialize_u128(u128));
313
314    write_atomic!(serialize_f32(f32));
315    write_atomic!(serialize_f64(f64));
316
317    fn serialize_char(self, value: char) -> Result<Self::Ok, Self::Error> {
318        self.serialize_str(value.encode_utf8(&mut [0u8; 4]))
319    }
320
321    fn serialize_str(mut self, value: &str) -> Result<Self::Ok, Self::Error> {
322        if !value.is_empty() {
323            self.write_delimiter()?;
324            escape_item(self.writer, value, self.target, self.level)?;
325        }
326        Ok(!value.is_empty())
327    }
328
329    fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, Self::Error> {
330        //TODO: Customization point - allow user to decide how to encode bytes
331        Err(SeError::Unsupported(
332            "`serialize_bytes` not supported yet".into(),
333        ))
334    }
335
336    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
337        Ok(false)
338    }
339
340    fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, Self::Error> {
341        value.serialize(self)
342    }
343
344    /// We cannot store anything, so the absence of a unit and presence of it
345    /// does not differ, so serialization of unit returns `Err(Unsupported)`
346    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
347        Err(SeError::Unsupported(
348            "cannot serialize unit type `()` as an `xs:list` item".into(),
349        ))
350    }
351
352    /// We cannot store anything, so the absence of a unit and presence of it
353    /// does not differ, so serialization of unit returns `Err(Unsupported)`
354    fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
355        Err(SeError::Unsupported(
356            format!(
357                "cannot serialize unit struct `{}` as an `xs:list` item",
358                name
359            )
360            .into(),
361        ))
362    }
363
364    fn serialize_unit_variant(
365        self,
366        _name: &'static str,
367        _variant_index: u32,
368        variant: &'static str,
369    ) -> Result<Self::Ok, Self::Error> {
370        self.serialize_str(variant)
371    }
372
373    fn serialize_newtype_struct<T: ?Sized + Serialize>(
374        self,
375        _name: &'static str,
376        value: &T,
377    ) -> Result<Self::Ok, Self::Error> {
378        value.serialize(self)
379    }
380
381    /// We cannot store both a variant discriminant and a variant value,
382    /// so serialization of enum newtype variant returns `Err(Unsupported)`
383    fn serialize_newtype_variant<T: ?Sized + Serialize>(
384        self,
385        name: &'static str,
386        _variant_index: u32,
387        variant: &'static str,
388        _value: &T,
389    ) -> Result<Self::Ok, SeError> {
390        Err(SeError::Unsupported(
391            format!(
392                "cannot serialize enum newtype variant `{}::{}` as an `xs:list` item",
393                name, variant
394            )
395            .into(),
396        ))
397    }
398
399    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
400        Err(SeError::Unsupported(
401            "cannot serialize sequence as an `xs:list` item".into(),
402        ))
403    }
404
405    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
406        Err(SeError::Unsupported(
407            "cannot serialize tuple as an `xs:list` item".into(),
408        ))
409    }
410
411    fn serialize_tuple_struct(
412        self,
413        name: &'static str,
414        _len: usize,
415    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
416        Err(SeError::Unsupported(
417            format!(
418                "cannot serialize tuple struct `{}` as an `xs:list` item",
419                name
420            )
421            .into(),
422        ))
423    }
424
425    fn serialize_tuple_variant(
426        self,
427        name: &'static str,
428        _variant_index: u32,
429        variant: &'static str,
430        _len: usize,
431    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
432        Err(SeError::Unsupported(
433            format!(
434                "cannot serialize enum tuple variant `{}::{}` as an `xs:list` item",
435                name, variant
436            )
437            .into(),
438        ))
439    }
440
441    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
442        Err(SeError::Unsupported(
443            "cannot serialize map as an `xs:list` item".into(),
444        ))
445    }
446
447    fn serialize_struct(
448        self,
449        name: &'static str,
450        _len: usize,
451    ) -> Result<Self::SerializeStruct, Self::Error> {
452        Err(SeError::Unsupported(
453            format!("cannot serialize struct `{}` as an `xs:list` item", name).into(),
454        ))
455    }
456
457    fn serialize_struct_variant(
458        self,
459        name: &'static str,
460        _variant_index: u32,
461        variant: &'static str,
462        _len: usize,
463    ) -> Result<Self::SerializeStructVariant, Self::Error> {
464        Err(SeError::Unsupported(
465            format!(
466                "cannot serialize enum struct variant `{}::{}` as an `xs:list` item",
467                name, variant
468            )
469            .into(),
470        ))
471    }
472}
473
474////////////////////////////////////////////////////////////////////////////////////////////////////
475
476/// A serializer for a values representing XSD [simple types], which used in:
477/// - attribute values (`<... ...="value" ...>`)
478/// - text content (`<...>text</...>`)
479/// - CDATA content (`<...><![CDATA[cdata]]></...>`)
480///
481/// [simple types]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
482pub struct SimpleTypeSerializer<W: Write> {
483    /// Writer to which this serializer writes content
484    pub writer: W,
485    /// Target for which element is serializing. Affects additional characters to escape.
486    pub target: QuoteTarget,
487    /// Defines which XML characters need to be escaped
488    pub level: QuoteLevel,
489}
490
491impl<W: Write> SimpleTypeSerializer<W> {
492    #[inline]
493    fn write_str(&mut self, value: &str) -> Result<(), SeError> {
494        Ok(self.writer.write_str(value)?)
495    }
496    #[inline]
497    fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), SeError> {
498        Ok(self.writer.write_fmt(args)?)
499    }
500}
501
502impl<W: Write> Serializer for SimpleTypeSerializer<W> {
503    type Ok = W;
504    type Error = SeError;
505
506    type SerializeSeq = SimpleSeq<W>;
507    type SerializeTuple = SimpleSeq<W>;
508    type SerializeTupleStruct = SimpleSeq<W>;
509    type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
510    type SerializeMap = Impossible<Self::Ok, Self::Error>;
511    type SerializeStruct = Impossible<Self::Ok, Self::Error>;
512    type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
513
514    write_primitive!();
515
516    fn serialize_str(mut self, value: &str) -> Result<Self::Ok, Self::Error> {
517        if !value.is_empty() {
518            escape_list(&mut self.writer, value, self.target, self.level)?;
519        }
520        Ok(self.writer)
521    }
522
523    /// Does not write anything
524    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
525        Ok(self.writer)
526    }
527
528    /// Does not write anything
529    fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
530        Ok(self.writer)
531    }
532
533    /// We cannot store both a variant discriminant and a variant value,
534    /// so serialization of enum newtype variant returns `Err(Unsupported)`
535    fn serialize_newtype_variant<T: ?Sized + Serialize>(
536        self,
537        name: &'static str,
538        _variant_index: u32,
539        variant: &'static str,
540        _value: &T,
541    ) -> Result<Self::Ok, SeError> {
542        Err(SeError::Unsupported(
543            format!("cannot serialize enum newtype variant `{}::{}` as an attribute or text content value", name, variant).into(),
544        ))
545    }
546
547    #[inline]
548    fn serialize_seq(mut self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
549        if let QuoteTarget::CData = self.target {
550            self.writer.write_str("<![CDATA[")?;
551        }
552        Ok(SimpleSeq {
553            writer: self.writer,
554            target: self.target,
555            level: self.level,
556            is_empty: true,
557        })
558    }
559
560    #[inline]
561    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
562        self.serialize_seq(None)
563    }
564
565    #[inline]
566    fn serialize_tuple_struct(
567        self,
568        _name: &'static str,
569        _len: usize,
570    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
571        self.serialize_seq(None)
572    }
573
574    fn serialize_tuple_variant(
575        self,
576        name: &'static str,
577        _variant_index: u32,
578        variant: &'static str,
579        _len: usize,
580    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
581        Err(SeError::Unsupported(
582            format!("cannot serialize enum tuple variant `{}::{}` as an attribute or text content value", name, variant).into(),
583        ))
584    }
585
586    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
587        Err(SeError::Unsupported(
588            "cannot serialize map as an attribute or text content value".into(),
589        ))
590    }
591
592    fn serialize_struct(
593        self,
594        name: &'static str,
595        _len: usize,
596    ) -> Result<Self::SerializeStruct, Self::Error> {
597        Err(SeError::Unsupported(
598            format!(
599                "cannot serialize struct `{}` as an attribute or text content value",
600                name
601            )
602            .into(),
603        ))
604    }
605
606    fn serialize_struct_variant(
607        self,
608        name: &'static str,
609        _variant_index: u32,
610        variant: &'static str,
611        _len: usize,
612    ) -> Result<Self::SerializeStructVariant, Self::Error> {
613        Err(SeError::Unsupported(
614            format!("cannot serialize enum struct variant `{}::{}` as an attribute or text content value", name, variant).into(),
615        ))
616    }
617}
618
619/// Serializer for a sequence of atomic values delimited by space
620pub struct SimpleSeq<W: Write> {
621    writer: W,
622    target: QuoteTarget,
623    level: QuoteLevel,
624    /// If `true`, nothing was written yet to the `writer`
625    is_empty: bool,
626}
627
628impl<W: Write> SerializeSeq for SimpleSeq<W> {
629    type Ok = W;
630    type Error = SeError;
631
632    fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
633    where
634        T: ?Sized + Serialize,
635    {
636        if value.serialize(AtomicSerializer {
637            writer: &mut self.writer,
638            target: self.target,
639            level: self.level,
640            write_delimiter: !self.is_empty,
641        })? {
642            self.is_empty = false;
643        }
644        Ok(())
645    }
646
647    #[inline]
648    fn end(mut self) -> Result<Self::Ok, Self::Error> {
649        if let QuoteTarget::CData = self.target {
650            self.writer.write_str("]]>")?;
651        }
652        Ok(self.writer)
653    }
654}
655
656impl<W: Write> SerializeTuple for SimpleSeq<W> {
657    type Ok = W;
658    type Error = SeError;
659
660    #[inline]
661    fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
662    where
663        T: ?Sized + Serialize,
664    {
665        SerializeSeq::serialize_element(self, value)
666    }
667
668    #[inline]
669    fn end(self) -> Result<Self::Ok, Self::Error> {
670        SerializeSeq::end(self)
671    }
672}
673
674impl<W: Write> SerializeTupleStruct for SimpleSeq<W> {
675    type Ok = W;
676    type Error = SeError;
677
678    #[inline]
679    fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
680    where
681        T: ?Sized + Serialize,
682    {
683        SerializeSeq::serialize_element(self, value)
684    }
685
686    #[inline]
687    fn end(self) -> Result<Self::Ok, Self::Error> {
688        SerializeSeq::end(self)
689    }
690}
691
692impl<W: Write> SerializeTupleVariant for SimpleSeq<W> {
693    type Ok = W;
694    type Error = SeError;
695
696    #[inline]
697    fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
698    where
699        T: ?Sized + Serialize,
700    {
701        SerializeSeq::serialize_element(self, value)
702    }
703
704    #[inline]
705    fn end(self) -> Result<Self::Ok, Self::Error> {
706        SerializeSeq::end(self)
707    }
708}
709
710////////////////////////////////////////////////////////////////////////////////////////////////////
711
712#[cfg(test)]
713mod tests {
714    use super::*;
715    use crate::utils::Bytes;
716    use serde::Serialize;
717    use std::collections::BTreeMap;
718
719    #[derive(Debug, Serialize, PartialEq)]
720    struct Unit;
721
722    #[derive(Debug, Serialize, PartialEq)]
723    struct Newtype(usize);
724
725    #[derive(Debug, Serialize, PartialEq)]
726    struct Tuple(&'static str, usize);
727
728    #[derive(Debug, Serialize, PartialEq)]
729    struct Struct {
730        key: &'static str,
731        val: usize,
732    }
733
734    #[derive(Debug, Serialize, PartialEq)]
735    enum Enum {
736        Unit,
737        #[serde(rename = "<\"&'>")]
738        UnitEscaped,
739        Newtype(usize),
740        Tuple(&'static str, usize),
741        Struct {
742            key: &'static str,
743            val: usize,
744        },
745    }
746
747    mod escape_item {
748        use super::*;
749        use pretty_assertions::assert_eq;
750
751        fn escape_item(value: &str, target: QuoteTarget, level: QuoteLevel) -> String {
752            let mut result = String::new();
753            super::escape_item(&mut result, value, target, level).unwrap();
754            result
755        }
756
757        mod full {
758            use super::*;
759            use pretty_assertions::assert_eq;
760
761            #[test]
762            fn text() {
763                assert_eq!(
764                    escape_item("text<\"'&> \t\n\rtext", QuoteTarget::Text, QuoteLevel::Full),
765                    "text&lt;&quot;&apos;&amp;&gt;&#32;&#9;&#10;&#13;text"
766                );
767            }
768
769            #[test]
770            fn double_quote_attr() {
771                assert_eq!(
772                    escape_item(
773                        "text<\"'&> \t\n\rtext",
774                        QuoteTarget::DoubleQAttr,
775                        QuoteLevel::Full
776                    ),
777                    "text&lt;&quot;&apos;&amp;&gt;&#32;&#9;&#10;&#13;text"
778                );
779            }
780
781            #[test]
782            fn single_quote_attr() {
783                assert_eq!(
784                    escape_item(
785                        "text<\"'&> \t\n\rtext",
786                        QuoteTarget::SingleQAttr,
787                        QuoteLevel::Full
788                    ),
789                    "text&lt;&quot;&apos;&amp;&gt;&#32;&#9;&#10;&#13;text"
790                );
791            }
792        }
793
794        mod partial {
795            use super::*;
796            use pretty_assertions::assert_eq;
797
798            #[test]
799            fn text() {
800                assert_eq!(
801                    escape_item(
802                        "text<\"'&> \t\n\rtext",
803                        QuoteTarget::Text,
804                        QuoteLevel::Partial
805                    ),
806                    "text&lt;\"'&amp;&gt;&#32;&#9;&#10;&#13;text"
807                );
808            }
809
810            #[test]
811            fn double_quote_attr() {
812                assert_eq!(
813                    escape_item(
814                        "text<\"'&> \t\n\rtext",
815                        QuoteTarget::DoubleQAttr,
816                        QuoteLevel::Partial
817                    ),
818                    "text&lt;&quot;'&amp;&gt;&#32;&#9;&#10;&#13;text"
819                );
820            }
821
822            #[test]
823            fn single_quote_attr() {
824                assert_eq!(
825                    escape_item(
826                        "text<\"'&> \t\n\rtext",
827                        QuoteTarget::SingleQAttr,
828                        QuoteLevel::Partial
829                    ),
830                    "text&lt;\"&apos;&amp;&gt;&#32;&#9;&#10;&#13;text"
831                );
832            }
833        }
834
835        mod minimal {
836            use super::*;
837            use pretty_assertions::assert_eq;
838
839            #[test]
840            fn text() {
841                assert_eq!(
842                    escape_item(
843                        "text<\"'&> \t\n\rtext",
844                        QuoteTarget::Text,
845                        QuoteLevel::Minimal
846                    ),
847                    "text&lt;\"'&amp;>&#32;&#9;&#10;&#13;text"
848                );
849            }
850
851            #[test]
852            fn double_quote_attr() {
853                assert_eq!(
854                    escape_item(
855                        "text<\"'&> \t\n\rtext",
856                        QuoteTarget::DoubleQAttr,
857                        QuoteLevel::Minimal
858                    ),
859                    "text&lt;&quot;'&amp;>&#32;&#9;&#10;&#13;text"
860                );
861            }
862
863            #[test]
864            fn single_quote_attr() {
865                assert_eq!(
866                    escape_item(
867                        "text<\"'&> \t\n\rtext",
868                        QuoteTarget::SingleQAttr,
869                        QuoteLevel::Minimal
870                    ),
871                    "text&lt;\"&apos;&amp;>&#32;&#9;&#10;&#13;text"
872                );
873            }
874        }
875
876        /// Escape function does not surround text with `<![CDATA[` and `]]>`, that should be done outside
877        #[test]
878        fn cdata() {
879            assert_eq!(
880                escape_item(
881                    "text<\"'&>]]> \t\n\rtext",
882                    QuoteTarget::CData,
883                    QuoteLevel::Full
884                ),
885                "text<\"'&>]]]]><![CDATA[> \t\n\rtext"
886            );
887            assert_eq!(
888                escape_item(
889                    "text<\"'&>]]> \t\n\rtext",
890                    QuoteTarget::CData,
891                    QuoteLevel::Partial
892                ),
893                "text<\"'&>]]]]><![CDATA[> \t\n\rtext"
894            );
895            assert_eq!(
896                escape_item(
897                    "text<\"'&>]]> \t\n\rtext",
898                    QuoteTarget::CData,
899                    QuoteLevel::Minimal
900                ),
901                "text<\"'&>]]]]><![CDATA[> \t\n\rtext"
902            );
903        }
904    }
905
906    mod escape_list {
907        use super::*;
908        use pretty_assertions::assert_eq;
909
910        fn escape_list(value: &str, target: QuoteTarget, level: QuoteLevel) -> String {
911            let mut result = String::new();
912            super::escape_list(&mut result, value, target, level).unwrap();
913            result
914        }
915
916        mod full {
917            use super::*;
918            use pretty_assertions::assert_eq;
919
920            #[test]
921            fn text() {
922                assert_eq!(
923                    escape_list("text<\"'&> \t\n\rtext", QuoteTarget::Text, QuoteLevel::Full),
924                    "text&lt;&quot;&apos;&amp;&gt; \t\n&#13;text"
925                );
926            }
927
928            #[test]
929            fn double_quote_attr() {
930                assert_eq!(
931                    escape_list(
932                        "text<\"'&> \t\n\rtext",
933                        QuoteTarget::DoubleQAttr,
934                        QuoteLevel::Full
935                    ),
936                    "text&lt;&quot;&apos;&amp;&gt; &#9;&#10;&#13;text"
937                );
938            }
939
940            #[test]
941            fn single_quote_attr() {
942                assert_eq!(
943                    escape_list(
944                        "text<\"'&> \t\n\rtext",
945                        QuoteTarget::SingleQAttr,
946                        QuoteLevel::Full
947                    ),
948                    "text&lt;&quot;&apos;&amp;&gt; &#9;&#10;&#13;text"
949                );
950            }
951        }
952
953        mod partial {
954            use super::*;
955            use pretty_assertions::assert_eq;
956
957            #[test]
958            fn text() {
959                assert_eq!(
960                    escape_list(
961                        "text<\"'&> \t\n\rtext",
962                        QuoteTarget::Text,
963                        QuoteLevel::Partial
964                    ),
965                    "text&lt;\"'&amp;&gt; \t\n&#13;text"
966                );
967            }
968
969            #[test]
970            fn double_quote_attr() {
971                assert_eq!(
972                    escape_list(
973                        "text<\"'&> \t\n\rtext",
974                        QuoteTarget::DoubleQAttr,
975                        QuoteLevel::Partial
976                    ),
977                    "text&lt;&quot;'&amp;&gt; &#9;&#10;&#13;text"
978                );
979            }
980
981            #[test]
982            fn single_quote_attr() {
983                assert_eq!(
984                    escape_list(
985                        "text<\"'&> \t\n\rtext",
986                        QuoteTarget::SingleQAttr,
987                        QuoteLevel::Partial
988                    ),
989                    "text&lt;\"&apos;&amp;&gt; &#9;&#10;&#13;text"
990                );
991            }
992        }
993
994        mod minimal {
995            use super::*;
996            use pretty_assertions::assert_eq;
997
998            #[test]
999            fn text() {
1000                assert_eq!(
1001                    escape_list(
1002                        "text<\"'&> \t\n\rtext",
1003                        QuoteTarget::Text,
1004                        QuoteLevel::Minimal
1005                    ),
1006                    "text&lt;\"'&amp;> \t\n&#13;text"
1007                );
1008            }
1009
1010            #[test]
1011            fn double_quote_attr() {
1012                assert_eq!(
1013                    escape_list(
1014                        "text<\"'&> \t\n\rtext",
1015                        QuoteTarget::DoubleQAttr,
1016                        QuoteLevel::Minimal
1017                    ),
1018                    "text&lt;&quot;'&amp;> &#9;&#10;&#13;text"
1019                );
1020            }
1021
1022            #[test]
1023            fn single_quote_attr() {
1024                assert_eq!(
1025                    escape_list(
1026                        "text<\"'&> \t\n\rtext",
1027                        QuoteTarget::SingleQAttr,
1028                        QuoteLevel::Minimal
1029                    ),
1030                    "text&lt;\"&apos;&amp;> &#9;&#10;&#13;text"
1031                );
1032            }
1033        }
1034
1035        #[test]
1036        fn cdata() {
1037            assert_eq!(
1038                escape_list(
1039                    "text<\"'&>]]> \t\n\rtext",
1040                    QuoteTarget::CData,
1041                    QuoteLevel::Full
1042                ),
1043                "<![CDATA[text<\"'&>]]]]><![CDATA[> \t\n\rtext]]>"
1044            );
1045            assert_eq!(
1046                escape_list(
1047                    "text<\"'&>]]> \t\n\rtext",
1048                    QuoteTarget::CData,
1049                    QuoteLevel::Partial
1050                ),
1051                "<![CDATA[text<\"'&>]]]]><![CDATA[> \t\n\rtext]]>"
1052            );
1053            assert_eq!(
1054                escape_list(
1055                    "text<\"'&>]]> \t\n\rtext",
1056                    QuoteTarget::CData,
1057                    QuoteLevel::Minimal
1058                ),
1059                "<![CDATA[text<\"'&>]]]]><![CDATA[> \t\n\rtext]]>"
1060            );
1061        }
1062    }
1063
1064    /// Tests for serialize atomic and union values, as defined in XSD specification
1065    mod atomic {
1066        use super::*;
1067        use pretty_assertions::assert_eq;
1068
1069        /// Checks that given `$data` successfully serialized as `$expected`
1070        macro_rules! serialize_as {
1071            ($name:ident: $data:expr => $expected:literal) => {
1072                #[test]
1073                fn $name() {
1074                    let mut buffer = String::new();
1075                    let ser = AtomicSerializer {
1076                        writer: &mut buffer,
1077                        target: QuoteTarget::Text,
1078                        level: QuoteLevel::Full,
1079                        write_delimiter: false,
1080                    };
1081
1082                    let has_written = $data.serialize(ser).unwrap();
1083                    assert_eq!(buffer, $expected);
1084                    assert_eq!(has_written, !buffer.is_empty());
1085                }
1086            };
1087        }
1088
1089        /// Checks that attempt to serialize given `$data` results to a
1090        /// serialization error `$kind` with `$reason`
1091        macro_rules! err {
1092            ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
1093                #[test]
1094                fn $name() {
1095                    let mut buffer = String::new();
1096                    let ser = AtomicSerializer {
1097                        writer: &mut buffer,
1098                        target: QuoteTarget::Text,
1099                        level: QuoteLevel::Full,
1100                        write_delimiter: false,
1101                    };
1102
1103                    match $data.serialize(ser).unwrap_err() {
1104                        SeError::$kind(e) => assert_eq!(e, $reason),
1105                        e => panic!(
1106                            "Expected `Err({}({}))`, but got `{:?}`",
1107                            stringify!($kind),
1108                            $reason,
1109                            e
1110                        ),
1111                    }
1112                    assert_eq!(buffer, "");
1113                }
1114            };
1115        }
1116
1117        serialize_as!(false_: false => "false");
1118        serialize_as!(true_:  true  => "true");
1119
1120        serialize_as!(i8_:    -42i8                => "-42");
1121        serialize_as!(i16_:   -4200i16             => "-4200");
1122        serialize_as!(i32_:   -42000000i32         => "-42000000");
1123        serialize_as!(i64_:   -42000000000000i64   => "-42000000000000");
1124        serialize_as!(isize_: -42000000isize       => "-42000000");
1125
1126        serialize_as!(u8_:    42u8                => "42");
1127        serialize_as!(u16_:   4200u16             => "4200");
1128        serialize_as!(u32_:   42000000u32         => "42000000");
1129        serialize_as!(u64_:   42000000000000u64   => "42000000000000");
1130        serialize_as!(usize_: 42000000usize       => "42000000");
1131
1132        serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
1133        serialize_as!(u128_:  420000000000000000000000000000u128 => "420000000000000000000000000000");
1134
1135        serialize_as!(f32_: 4.2f32 => "4.2");
1136        serialize_as!(f64_: 4.2f64 => "4.2");
1137
1138        serialize_as!(char_non_escaped: 'h' => "h");
1139        serialize_as!(char_lt:   '<' => "&lt;");
1140        serialize_as!(char_gt:   '>' => "&gt;");
1141        serialize_as!(char_amp:  '&' => "&amp;");
1142        serialize_as!(char_apos: '\'' => "&apos;");
1143        serialize_as!(char_quot: '"' => "&quot;");
1144
1145        serialize_as!(str_non_escaped: "non-escaped-string" => "non-escaped-string");
1146        serialize_as!(str_escaped: "<\"escaped & string'>" => "&lt;&quot;escaped&#32;&amp;&#32;string&apos;&gt;");
1147
1148        err!(bytes: Bytes(b"<\"escaped & bytes'>")
1149            => Unsupported("`serialize_bytes` not supported yet"));
1150
1151        serialize_as!(option_none: Option::<&str>::None => "");
1152        serialize_as!(option_some: Some("non-escaped-string") => "non-escaped-string");
1153
1154        err!(unit: ()
1155            => Unsupported("cannot serialize unit type `()` as an `xs:list` item"));
1156        err!(unit_struct: Unit
1157            => Unsupported("cannot serialize unit struct `Unit` as an `xs:list` item"));
1158
1159        serialize_as!(enum_unit: Enum::Unit => "Unit");
1160        serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "&lt;&quot;&amp;&apos;&gt;");
1161
1162        serialize_as!(newtype: Newtype(42) => "42");
1163        err!(enum_newtype: Enum::Newtype(42)
1164            => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an `xs:list` item"));
1165
1166        err!(seq: vec![1, 2, 3]
1167            => Unsupported("cannot serialize sequence as an `xs:list` item"));
1168        err!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize)
1169            => Unsupported("cannot serialize tuple as an `xs:list` item"));
1170        err!(tuple_struct: Tuple("first", 42)
1171            => Unsupported("cannot serialize tuple struct `Tuple` as an `xs:list` item"));
1172        err!(enum_tuple: Enum::Tuple("first", 42)
1173            => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an `xs:list` item"));
1174
1175        err!(map: BTreeMap::from([(1, 2), (3, 4)])
1176            => Unsupported("cannot serialize map as an `xs:list` item"));
1177        err!(struct_: Struct { key: "answer", val: 42 }
1178            => Unsupported("cannot serialize struct `Struct` as an `xs:list` item"));
1179        err!(enum_struct: Enum::Struct { key: "answer", val: 42 }
1180            => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an `xs:list` item"));
1181    }
1182
1183    mod simple_type {
1184        use super::*;
1185        use pretty_assertions::assert_eq;
1186
1187        /// Checks that given `$data` successfully serialized as `$expected`
1188        macro_rules! serialize_as {
1189            ($name:ident: $data:expr => $expected:literal) => {
1190                #[test]
1191                fn $name() {
1192                    let ser = SimpleTypeSerializer {
1193                        writer: String::new(),
1194                        target: QuoteTarget::Text,
1195                        level: QuoteLevel::Full,
1196                    };
1197
1198                    let buffer = $data.serialize(ser).unwrap();
1199                    assert_eq!(buffer, $expected);
1200                }
1201            };
1202        }
1203
1204        /// Checks that attempt to serialize given `$data` results to a
1205        /// serialization error `$kind` with `$reason`
1206        macro_rules! err {
1207            ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
1208                #[test]
1209                fn $name() {
1210                    let mut buffer = String::new();
1211                    let ser = SimpleTypeSerializer {
1212                        writer: &mut buffer,
1213                        target: QuoteTarget::Text,
1214                        level: QuoteLevel::Full,
1215                    };
1216
1217                    match $data.serialize(ser).unwrap_err() {
1218                        SeError::$kind(e) => assert_eq!(e, $reason),
1219                        e => panic!(
1220                            "Expected `Err({}({}))`, but got `{:?}`",
1221                            stringify!($kind),
1222                            $reason,
1223                            e
1224                        ),
1225                    }
1226                    assert_eq!(buffer, "");
1227                }
1228            };
1229        }
1230
1231        serialize_as!(false_: false => "false");
1232        serialize_as!(true_:  true  => "true");
1233
1234        serialize_as!(i8_:    -42i8                => "-42");
1235        serialize_as!(i16_:   -4200i16             => "-4200");
1236        serialize_as!(i32_:   -42000000i32         => "-42000000");
1237        serialize_as!(i64_:   -42000000000000i64   => "-42000000000000");
1238        serialize_as!(isize_: -42000000isize       => "-42000000");
1239
1240        serialize_as!(u8_:    42u8                => "42");
1241        serialize_as!(u16_:   4200u16             => "4200");
1242        serialize_as!(u32_:   42000000u32         => "42000000");
1243        serialize_as!(u64_:   42000000000000u64   => "42000000000000");
1244        serialize_as!(usize_: 42000000usize       => "42000000");
1245
1246        serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
1247        serialize_as!(u128_:  420000000000000000000000000000u128 => "420000000000000000000000000000");
1248
1249        serialize_as!(f32_: 4.2f32 => "4.2");
1250        serialize_as!(f64_: 4.2f64 => "4.2");
1251
1252        serialize_as!(char_non_escaped: 'h' => "h");
1253        serialize_as!(char_lt:   '<' => "&lt;");
1254        serialize_as!(char_gt:   '>' => "&gt;");
1255        serialize_as!(char_amp:  '&' => "&amp;");
1256        serialize_as!(char_apos: '\'' => "&apos;");
1257        serialize_as!(char_quot: '"' => "&quot;");
1258
1259        serialize_as!(str_non_escaped: "non-escaped string" => "non-escaped string");
1260        serialize_as!(str_escaped: "<\"escaped & string'>" => "&lt;&quot;escaped &amp; string&apos;&gt;");
1261
1262        err!(bytes: Bytes(b"<\"escaped & bytes'>")
1263            => Unsupported("`serialize_bytes` not supported yet"));
1264
1265        serialize_as!(option_none: Option::<&str>::None => "");
1266        serialize_as!(option_some: Some("non-escaped string") => "non-escaped string");
1267
1268        serialize_as!(unit: () => "");
1269        serialize_as!(unit_struct: Unit => "");
1270
1271        serialize_as!(enum_unit: Enum::Unit => "Unit");
1272        serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "&lt;&quot;&amp;&apos;&gt;");
1273
1274        serialize_as!(newtype: Newtype(42) => "42");
1275        err!(enum_newtype: Enum::Newtype(42)
1276            => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value"));
1277
1278        serialize_as!(seq: vec![1, 2, 3] => "1 2 3");
1279        serialize_as!(seq_empty: Vec::<usize>::new() => "");
1280        serialize_as!(seq_with_1_empty_str: vec![""] => "");
1281        serialize_as!(seq_with_2_empty_strs: vec!["", ""] => "");
1282        serialize_as!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize)
1283            => "&lt;&quot;&amp;&apos;&gt; with&#9;&#10;&#13;&#32;spaces 3");
1284        serialize_as!(tuple_struct: Tuple("first", 42) => "first 42");
1285        err!(enum_tuple: Enum::Tuple("first", 42)
1286            => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value"));
1287
1288        err!(map: BTreeMap::from([(1, 2), (3, 4)])
1289            => Unsupported("cannot serialize map as an attribute or text content value"));
1290        err!(struct_: Struct { key: "answer", val: 42 }
1291            => Unsupported("cannot serialize struct `Struct` as an attribute or text content value"));
1292        err!(enum_struct: Enum::Struct { key: "answer", val: 42 }
1293            => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value"));
1294    }
1295
1296    mod simple_seq {
1297        use super::*;
1298        use pretty_assertions::assert_eq;
1299
1300        #[test]
1301        fn empty_seq() {
1302            let mut buffer = String::new();
1303            let ser = SimpleSeq {
1304                writer: &mut buffer,
1305                target: QuoteTarget::Text,
1306                level: QuoteLevel::Full,
1307                is_empty: true,
1308            };
1309
1310            SerializeSeq::end(ser).unwrap();
1311            assert_eq!(buffer, "");
1312        }
1313
1314        #[test]
1315        fn all_items_empty() {
1316            let mut buffer = String::new();
1317            let mut ser = SimpleSeq {
1318                writer: &mut buffer,
1319                target: QuoteTarget::Text,
1320                level: QuoteLevel::Full,
1321                is_empty: true,
1322            };
1323
1324            SerializeSeq::serialize_element(&mut ser, "").unwrap();
1325            SerializeSeq::serialize_element(&mut ser, "").unwrap();
1326            SerializeSeq::serialize_element(&mut ser, "").unwrap();
1327            SerializeSeq::end(ser).unwrap();
1328            assert_eq!(buffer, "");
1329        }
1330
1331        #[test]
1332        fn some_items_empty1() {
1333            let mut buffer = String::new();
1334            let mut ser = SimpleSeq {
1335                writer: &mut buffer,
1336                target: QuoteTarget::Text,
1337                level: QuoteLevel::Full,
1338                is_empty: true,
1339            };
1340
1341            SerializeSeq::serialize_element(&mut ser, "").unwrap();
1342            SerializeSeq::serialize_element(&mut ser, &1).unwrap();
1343            SerializeSeq::serialize_element(&mut ser, "").unwrap();
1344            SerializeSeq::end(ser).unwrap();
1345            assert_eq!(buffer, "1");
1346        }
1347
1348        #[test]
1349        fn some_items_empty2() {
1350            let mut buffer = String::new();
1351            let mut ser = SimpleSeq {
1352                writer: &mut buffer,
1353                target: QuoteTarget::Text,
1354                level: QuoteLevel::Full,
1355                is_empty: true,
1356            };
1357
1358            SerializeSeq::serialize_element(&mut ser, &1).unwrap();
1359            SerializeSeq::serialize_element(&mut ser, "").unwrap();
1360            SerializeSeq::serialize_element(&mut ser, &2).unwrap();
1361            SerializeSeq::end(ser).unwrap();
1362            assert_eq!(buffer, "1 2");
1363        }
1364
1365        #[test]
1366        fn items() {
1367            let mut buffer = String::new();
1368            let mut ser = SimpleSeq {
1369                writer: &mut buffer,
1370                target: QuoteTarget::Text,
1371                level: QuoteLevel::Full,
1372                is_empty: true,
1373            };
1374
1375            SerializeSeq::serialize_element(&mut ser, &1).unwrap();
1376            SerializeSeq::serialize_element(&mut ser, &2).unwrap();
1377            SerializeSeq::serialize_element(&mut ser, &3).unwrap();
1378            SerializeSeq::end(ser).unwrap();
1379            assert_eq!(buffer, "1 2 3");
1380        }
1381    }
1382
1383    mod cdata {
1384        use super::*;
1385        use pretty_assertions::assert_eq;
1386
1387        macro_rules! serialize_as_cdata {
1388            ($name:ident: $data:expr => $expected:literal) => {
1389                #[test]
1390                fn $name() {
1391                    let ser = SimpleTypeSerializer {
1392                        writer: String::new(),
1393                        target: QuoteTarget::CData,
1394                        level: QuoteLevel::Full,
1395                    };
1396
1397                    let buffer = $data.serialize(ser).unwrap();
1398                    assert_eq!(buffer, $expected);
1399                }
1400            };
1401        }
1402
1403        serialize_as_cdata!(empty_string: "" => "");
1404        serialize_as_cdata!(simple_text: "Hello World" => "<![CDATA[Hello World]]>");
1405        serialize_as_cdata!(with_markup: "<tag>content</tag>" => "<![CDATA[<tag>content</tag>]]>");
1406        serialize_as_cdata!(with_ampersand: "Tom & Jerry" => "<![CDATA[Tom & Jerry]]>");
1407        serialize_as_cdata!(with_quotes: r#"He said "Hello""# => r#"<![CDATA[He said "Hello"]]>"#);
1408        serialize_as_cdata!(all_xml_chars: "<>&\"'" => "<![CDATA[<>&\"']]>");
1409
1410        serialize_as_cdata!(with_cdata_end: "foo]]>bar" => "<![CDATA[foo]]]]><![CDATA[>bar]]>");
1411        serialize_as_cdata!(multiple_cdata_ends: "a]]>b]]>c" => "<![CDATA[a]]]]><![CDATA[>b]]]]><![CDATA[>c]]>");
1412        serialize_as_cdata!(starts_with_cdata_end: "]]>hello" => "<![CDATA[]]]]><![CDATA[>hello]]>");
1413        serialize_as_cdata!(ends_with_cdata_end: "hello]]>" => "<![CDATA[hello]]]]><![CDATA[>]]>");
1414        serialize_as_cdata!(only_cdata_end: "]]>" => "<![CDATA[]]]]><![CDATA[>]]>");
1415
1416        serialize_as_cdata!(seq_basic: vec!["foo", "bar", "baz"] => "<![CDATA[foo bar baz]]>");
1417        serialize_as_cdata!(seq_with_space: vec!["hello world", "hello\tworld", "world"] => "<![CDATA[hello world hello\tworld world]]>");
1418        serialize_as_cdata!(seq_with_markup_chars: vec!["<tag>", "&entity", "\"quoted\""] => "<![CDATA[<tag> &entity \"quoted\"]]>");
1419        serialize_as_cdata!(seq_with_cdata_end_split: vec!["foo]]>bar", "test"] => "<![CDATA[foo]]]]><![CDATA[>bar test]]>");
1420        serialize_as_cdata!(tuple_cdata: ("first", 42, "third") => "<![CDATA[first 42 third]]>");
1421    }
1422}