Skip to main content

oxixml_xml/se/
mod.rs

1//! Serde serializer that turns Rust values into XML.
2//!
3//! This module mirrors the `quick_xml::se` module of quick-xml 0.41: the entry
4//! points are [`to_string`], [`to_string_with_root`], [`to_writer`],
5//! [`to_writer_with_root`] and [`to_utf8_io_writer`], all of which are thin
6//! wrappers around [`Serializer`].
7//!
8//! # Mapping rules
9//!
10//! The mapping is exactly symmetric to the one implemented by the
11//! [deserializer](crate::de):
12//!
13//! * a struct becomes an element whose name is the struct name (or the root tag
14//!   passed to [`Serializer::with_root`]);
15//! * a field renamed to `@name` becomes the attribute `name` of that element;
16//! * a field renamed to [`$text`](crate::de) becomes the text content of the
17//!   element;
18//! * a field renamed to `$value` becomes the content of the element *without* an
19//!   extra wrapping tag, which is how enums and mixed content are represented;
20//! * every other field becomes a child element named after the field;
21//! * a sequence repeats the element for each item; an empty sequence writes
22//!   nothing;
23//! * [`None`] and `()` are written as an empty element, whose exact shape is
24//!   controlled by [`Serializer::empty_element_handling`];
25//! * enum unit variants are written as `<Variant/>` in `$value` position and as
26//!   the variant name in text/attribute position; newtype, tuple and struct
27//!   variants are written as `<Variant>…</Variant>`.
28//!
29//! Attribute values are escaped so that `"` never appears literally, while text
30//! content leaves quotes alone; the exact set of escaped characters is chosen by
31//! [`QuoteLevel`] (see [`Serializer::set_quote_level`]).
32//!
33//! # Example
34//!
35//! ```
36//! use oxixml_xml::se::to_string;
37//! use serde::Serialize;
38//!
39//! #[derive(Serialize)]
40//! struct Translation<'a> {
41//!     #[serde(rename = "@lang")]
42//!     lang: &'a str,
43//!     #[serde(rename = "$text")]
44//!     text: &'a str,
45//! }
46//!
47//! let xml = to_string(&Translation {
48//!     lang: "en",
49//!     text: "cats & dogs",
50//! })
51//! .expect("serializable");
52//!
53//! assert_eq!(xml, r#"<Translation lang="en">cats &amp; dogs</Translation>"#);
54//! ```
55
56/// Implements writing primitives to the underlying writer.
57/// Implementor must provide `write_str(self, &str) -> Result<(), DeError>` method
58macro_rules! write_primitive {
59    ($method:ident ( $ty:ty )) => {
60        fn $method(mut self, value: $ty) -> Result<Self::Ok, Self::Error> {
61            self.write_fmt(format_args!("{}", value))?;
62            Ok(self.writer)
63        }
64    };
65    () => {
66        fn serialize_bool(mut self, value: bool) -> Result<Self::Ok, Self::Error> {
67            self.write_str(if value { "true" } else { "false" })?;
68            Ok(self.writer)
69        }
70
71        write_primitive!(serialize_i8(i8));
72        write_primitive!(serialize_i16(i16));
73        write_primitive!(serialize_i32(i32));
74        write_primitive!(serialize_i64(i64));
75
76        write_primitive!(serialize_u8(u8));
77        write_primitive!(serialize_u16(u16));
78        write_primitive!(serialize_u32(u32));
79        write_primitive!(serialize_u64(u64));
80
81        write_primitive!(serialize_i128(i128));
82        write_primitive!(serialize_u128(u128));
83
84        write_primitive!(serialize_f32(f32));
85        write_primitive!(serialize_f64(f64));
86
87        fn serialize_char(self, value: char) -> Result<Self::Ok, Self::Error> {
88            self.serialize_str(value.encode_utf8(&mut [0u8; 4]))
89        }
90
91        fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, Self::Error> {
92            // There is no canonical XML representation of a byte string, so
93            // callers must pick one explicitly (e.g. base64) and serialize a string.
94            Err(Self::Error::Unsupported(
95                "`serialize_bytes` not supported yet".into(),
96            ))
97        }
98
99        fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
100            Ok(self.writer)
101        }
102
103        fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, Self::Error> {
104            value.serialize(self)
105        }
106
107        fn serialize_unit_variant(
108            self,
109            _name: &'static str,
110            _variant_index: u32,
111            variant: &'static str,
112        ) -> Result<Self::Ok, Self::Error> {
113            self.serialize_str(variant)
114        }
115
116        fn serialize_newtype_struct<T: ?Sized + Serialize>(
117            self,
118            _name: &'static str,
119            value: &T,
120        ) -> Result<Self::Ok, Self::Error> {
121            value.serialize(self)
122        }
123    };
124}
125
126////////////////////////////////////////////////////////////////////////////////////////////////////
127
128mod content;
129mod element;
130pub(crate) mod key;
131pub(crate) mod simple_type;
132mod text;
133
134use self::content::ContentSerializer;
135use self::element::{ElementSerializer, Map, Struct, Tuple};
136use crate::de::TEXT_KEY;
137use crate::writer::{Indentation, ToFmtWrite};
138use serde::ser::{self, Serialize};
139use std::fmt::Write;
140use std::str::from_utf8;
141
142pub use self::simple_type::SimpleTypeSerializer;
143pub use crate::errors::serialize::SeError;
144
145/// Serialize struct into a `Write`r.
146///
147/// Returns the classification of the last written type.
148///
149/// # Examples
150///
151/// ```
152/// # use oxixml_xml::se::to_writer;
153/// # use serde::Serialize;
154/// #[derive(Serialize)]
155/// struct Root<'a> {
156///     #[serde(rename = "@attribute")]
157///     attribute: &'a str,
158///     element: &'a str,
159///     #[serde(rename = "$text")]
160///     text: &'a str,
161/// }
162///
163/// let data = Root {
164///     attribute: "attribute content",
165///     element: "element content",
166///     text: "text content",
167/// };
168///
169/// let mut buffer = String::new();
170/// to_writer(&mut buffer, &data).expect("serialization should succeed");
171/// assert_eq!(
172///     buffer,
173///     // The root tag name is automatically deduced from the struct name
174///     // This will not work for other types or struct with #[serde(flatten)] fields
175///     "<Root attribute=\"attribute content\">\
176///         <element>element content</element>\
177///         text content\
178///     </Root>"
179/// );
180/// ```
181pub fn to_writer<W, T>(mut writer: W, value: &T) -> Result<WriteResult, SeError>
182where
183    W: Write,
184    T: ?Sized + Serialize,
185{
186    value.serialize(Serializer::new(&mut writer))
187}
188
189/// Serialize struct into a `io::Write`r restricted to utf-8 encoding.
190///
191/// Returns the classification of the last written type.
192///
193/// # Examples
194///
195/// ```
196/// # use oxixml_xml::se::to_utf8_io_writer;
197/// # use serde::Serialize;
198/// # use std::io::BufWriter;
199/// #[derive(Serialize)]
200/// struct Root<'a> {
201///     #[serde(rename = "@attribute")]
202///     attribute: &'a str,
203///     element: &'a str,
204///     #[serde(rename = "$text")]
205///     text: &'a str,
206/// }
207///
208/// let data = Root {
209///     attribute: "attribute content",
210///     element: "element content",
211///     text: "text content",
212/// };
213///
214/// let mut buffer = Vec::new();
215/// to_utf8_io_writer(&mut BufWriter::new(&mut buffer), &data).expect("serialization should succeed");
216///
217/// assert_eq!(
218///     std::str::from_utf8(&buffer).expect("valid UTF-8"),
219///     // The root tag name is automatically deduced from the struct name
220///     // This will not work for other types or struct with #[serde(flatten)] fields
221///     "<Root attribute=\"attribute content\">\
222///         <element>element content</element>\
223///         text content\
224///     </Root>"
225/// );
226/// ```
227pub fn to_utf8_io_writer<W, T>(writer: W, value: &T) -> Result<WriteResult, SeError>
228where
229    W: std::io::Write,
230    T: ?Sized + Serialize,
231{
232    value.serialize(Serializer::new(&mut ToFmtWrite(writer)))
233}
234
235/// Serialize struct into a `String`.
236///
237/// # Examples
238///
239/// ```
240/// # use oxixml_xml::se::to_string;
241/// # use serde::Serialize;
242/// #[derive(Serialize)]
243/// struct Root<'a> {
244///     #[serde(rename = "@attribute")]
245///     attribute: &'a str,
246///     element: &'a str,
247///     #[serde(rename = "$text")]
248///     text: &'a str,
249/// }
250///
251/// let data = Root {
252///     attribute: "attribute content",
253///     element: "element content",
254///     text: "text content",
255/// };
256///
257/// assert_eq!(
258///     to_string(&data).expect("serialization should succeed"),
259///     // The root tag name is automatically deduced from the struct name
260///     // This will not work for other types or struct with #[serde(flatten)] fields
261///     "<Root attribute=\"attribute content\">\
262///         <element>element content</element>\
263///         text content\
264///     </Root>"
265/// );
266/// ```
267pub fn to_string<T>(value: &T) -> Result<String, SeError>
268where
269    T: ?Sized + Serialize,
270{
271    let mut buffer = String::new();
272    to_writer(&mut buffer, value)?;
273    Ok(buffer)
274}
275
276/// Serialize struct into a `Write`r using specified root tag name.
277/// `root_tag` should be valid [XML name], otherwise error is returned.
278///
279/// Returns the classification of the last written type.
280///
281/// # Examples
282///
283/// ```
284/// # use oxixml_xml::se::to_writer_with_root;
285/// # use serde::Serialize;
286/// #[derive(Serialize)]
287/// struct Root<'a> {
288///     #[serde(rename = "@attribute")]
289///     attribute: &'a str,
290///     element: &'a str,
291///     #[serde(rename = "$text")]
292///     text: &'a str,
293/// }
294///
295/// let data = Root {
296///     attribute: "attribute content",
297///     element: "element content",
298///     text: "text content",
299/// };
300///
301/// let mut buffer = String::new();
302/// to_writer_with_root(&mut buffer, "top-level", &data).expect("serialization should succeed");
303/// assert_eq!(
304///     buffer,
305///     "<top-level attribute=\"attribute content\">\
306///         <element>element content</element>\
307///         text content\
308///     </top-level>"
309/// );
310/// ```
311///
312/// [XML name]: https://www.w3.org/TR/xml11/#NT-Name
313pub fn to_writer_with_root<W, T>(
314    mut writer: W,
315    root_tag: &str,
316    value: &T,
317) -> Result<WriteResult, SeError>
318where
319    W: Write,
320    T: ?Sized + Serialize,
321{
322    value.serialize(Serializer::with_root(&mut writer, Some(root_tag))?)
323}
324
325/// Serialize struct into a `String` using specified root tag name.
326/// `root_tag` should be valid [XML name], otherwise error is returned.
327///
328/// # Examples
329///
330/// ```
331/// # use oxixml_xml::se::to_string_with_root;
332/// # use serde::Serialize;
333/// #[derive(Serialize)]
334/// struct Root<'a> {
335///     #[serde(rename = "@attribute")]
336///     attribute: &'a str,
337///     element: &'a str,
338///     #[serde(rename = "$text")]
339///     text: &'a str,
340/// }
341///
342/// let data = Root {
343///     attribute: "attribute content",
344///     element: "element content",
345///     text: "text content",
346/// };
347///
348/// assert_eq!(
349///     to_string_with_root("top-level", &data).expect("serialization should succeed"),
350///     "<top-level attribute=\"attribute content\">\
351///         <element>element content</element>\
352///         text content\
353///     </top-level>"
354/// );
355/// ```
356///
357/// [XML name]: https://www.w3.org/TR/xml11/#NT-Name
358pub fn to_string_with_root<T>(root_tag: &str, value: &T) -> Result<String, SeError>
359where
360    T: ?Sized + Serialize,
361{
362    let mut buffer = String::new();
363    to_writer_with_root(&mut buffer, root_tag, value)?;
364    Ok(buffer)
365}
366
367////////////////////////////////////////////////////////////////////////////////////////////////////
368
369/// Defines the format for text content serialization
370#[derive(Debug, Clone, Copy, PartialEq, Eq)]
371#[non_exhaustive]
372pub enum TextFormat {
373    /// Serialize as regular text content with escaping
374    Text,
375    /// Serialize as CDATA section without escaping
376    CData,
377}
378
379/// Defines which characters would be escaped in [`Text`] events and attribute
380/// values.
381///
382/// [`Text`]: crate::events::Event::Text
383#[derive(Debug, Clone, Copy, PartialEq, Eq)]
384pub enum QuoteLevel {
385    /// Performs escaping, escape all characters that could have special meaning
386    /// in the XML. This mode is compatible with SGML specification.
387    ///
388    /// Characters that will be replaced:
389    ///
390    /// Original | Replacement
391    /// ---------|------------
392    /// `<`      | `&lt;`
393    /// `>`      | `&gt;`
394    /// `&`      | `&amp;`
395    /// `"`      | `&quot;`
396    /// `'`      | `&apos;`
397    Full,
398    /// Performs escaping that is compatible with SGML specification.
399    ///
400    /// This level adds escaping of `>` to the `Minimal` level, which is [required]
401    /// for compatibility with SGML.
402    ///
403    /// Characters that will be replaced:
404    ///
405    /// Original | Replacement
406    /// ---------|------------
407    /// `<`      | `&lt;`
408    /// `>`      | `&gt;`
409    /// `&`      | `&amp;`
410    ///
411    /// [required]: https://www.w3.org/TR/xml11/#syntax
412    Partial,
413    /// Performs the minimal possible escaping, escape only strictly necessary
414    /// characters.
415    ///
416    /// Characters that will be replaced:
417    ///
418    /// Original | Replacement
419    /// ---------|------------
420    /// `<`      | `&lt;`
421    /// `&`      | `&amp;`
422    Minimal,
423}
424
425/// Specifies how empty elements are serialized.
426/// The default is self-closed with no space before the slash.
427#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
428pub enum EmptyElementHandling {
429    /// Empty elements will be written as `<element/>`. This is the default behavior.
430    #[default]
431    SelfClosed,
432
433    /// The same, as [`SelfClosed`], but an extra space will be written before
434    /// the slash, so empty elements will be written as `<element />`.
435    /// This is recommended by the [W3C guidelines] for XHTML.
436    ///
437    /// [`SelfClosed`]: Self::SelfClosed
438    /// [W3C guidelines]: https://www.w3.org/TR/xhtml1/#guidelines
439    SelfClosedWithSpace,
440
441    /// Empty elements will be expanded, as in: `<element></element>`.
442    Expanded,
443}
444
445/// Classification of the type written by the serializer.
446#[derive(Debug, Clone, Copy, PartialEq, Eq)]
447pub enum WriteResult {
448    /// Text with insignificant spaces was written, for example a number. Adding indent to the
449    /// serialized data does not change meaning of the data.
450    Text,
451    /// The XML tag was written. Adding indent to the serialized data does not change meaning of the data.
452    Element,
453    /// Nothing was written (i. e. serialized type not represented in XML a all). Adding indent to the
454    /// serialized data does not change meaning of the data. This is returned for units, unit structs
455    /// and unit variants.
456    Nothing,
457    /// Text with significant spaces was written, for example a string. Adding indent to the
458    /// serialized data may change meaning of the data.
459    SensitiveText,
460    /// `None` was serialized and nothing was written. `None` does not represented in XML,
461    /// but adding indent after it may change meaning of the data.
462    SensitiveNothing,
463}
464
465impl WriteResult {
466    /// Returns `true` if indent should be written after the object (if configured) and `false` otherwise.
467    #[inline]
468    pub fn allow_indent(&self) -> bool {
469        matches!(self, Self::Element | Self::Nothing)
470    }
471
472    /// Returns `true` if self is `Text` or `SensitiveText`.
473    #[inline]
474    pub fn is_text(&self) -> bool {
475        matches!(self, Self::Text | Self::SensitiveText)
476    }
477}
478
479////////////////////////////////////////////////////////////////////////////////////////////////////
480
481/// Implements serialization method by forwarding it to the serializer created by
482/// the helper method [`Serializer::ser`].
483macro_rules! forward {
484    ($name:ident($ty:ty)) => {
485        fn $name(self, value: $ty) -> Result<Self::Ok, Self::Error> {
486            self.ser(&concat!("`", stringify!($ty), "`"))?.$name(value)
487        }
488    };
489}
490
491////////////////////////////////////////////////////////////////////////////////////////////////////
492
493/// Almost all characters can form a name. Citation from <https://www.w3.org/TR/xml11/#sec-xml11>:
494///
495/// > The overall philosophy of names has changed since XML 1.0. Whereas XML 1.0
496/// > provided a rigid definition of names, wherein everything that was not permitted
497/// > was forbidden, XML 1.1 names are designed so that everything that is not
498/// > forbidden (for a specific reason) is permitted. Since Unicode will continue
499/// > to grow past version 4.0, further changes to XML can be avoided by allowing
500/// > almost any character, including those not yet assigned, in names.
501///
502/// <https://www.w3.org/TR/xml11/#NT-NameStartChar>
503const fn is_xml11_name_start_char(ch: char) -> bool {
504    // Not need to use macro when core primitives is enough
505    #[allow(clippy::match_like_matches_macro)]
506    match ch {
507        ':'
508        | 'A'..='Z'
509        | '_'
510        | 'a'..='z'
511        | '\u{00C0}'..='\u{00D6}'
512        | '\u{00D8}'..='\u{00F6}'
513        | '\u{00F8}'..='\u{02FF}'
514        | '\u{0370}'..='\u{037D}'
515        | '\u{037F}'..='\u{1FFF}'
516        | '\u{200C}'..='\u{200D}'
517        | '\u{2070}'..='\u{218F}'
518        | '\u{2C00}'..='\u{2FEF}'
519        | '\u{3001}'..='\u{D7FF}'
520        | '\u{F900}'..='\u{FDCF}'
521        | '\u{FDF0}'..='\u{FFFD}'
522        | '\u{10000}'..='\u{EFFFF}' => true,
523        _ => false,
524    }
525}
526/// <https://www.w3.org/TR/xml11/#NT-NameChar>
527const fn is_xml11_name_char(ch: char) -> bool {
528    match ch {
529        '-' | '.' | '0'..='9' | '\u{00B7}' | '\u{0300}'..='\u{036F}' | '\u{203F}'..='\u{2040}' => {
530            true
531        }
532        _ => is_xml11_name_start_char(ch),
533    }
534}
535
536/// Helper struct to self-defense from errors
537#[derive(Clone, Copy, Debug, PartialEq)]
538struct XmlName<'n>(&'n str);
539
540impl<'n> XmlName<'n> {
541    /// Checks correctness of the XML name according to [XML 1.1 specification]
542    ///
543    /// [XML 1.1 specification]: https://www.w3.org/TR/xml11/#NT-Name
544    pub fn try_from(name: &'n str) -> Result<XmlName<'n>, SeError> {
545        // Invalid names are rejected rather than mangled, so that a serialization
546        // failure is never silently turned into a differently named element.
547        match name.chars().next() {
548            Some(ch) if !is_xml11_name_start_char(ch) => Err(SeError::Unsupported(
549                format!("character `{ch}` is not allowed at the start of an XML name `{name}`")
550                    .into(),
551            )),
552            _ => match name.matches(|ch| !is_xml11_name_char(ch)).next() {
553                Some(s) => Err(SeError::Unsupported(
554                    format!("character `{s}` is not allowed in an XML name `{name}`").into(),
555                )),
556                None => Ok(XmlName(name)),
557            },
558        }
559    }
560}
561
562////////////////////////////////////////////////////////////////////////////////////////////////////
563
564pub(crate) enum Indent<'i> {
565    /// No indent should be written before the element
566    None,
567    /// The specified indent should be written. The type owns the buffer with indent
568    Owned(Indentation),
569    /// The specified indent should be written. The type borrows buffer with indent
570    /// from its owner
571    Borrow(&'i mut Indentation),
572}
573
574impl<'i> Indent<'i> {
575    pub fn borrow(&mut self) -> Indent<'_> {
576        match self {
577            Self::None => Indent::None,
578            Self::Owned(i) => Indent::Borrow(i),
579            Self::Borrow(i) => Indent::Borrow(i),
580        }
581    }
582
583    pub fn increase(&mut self) {
584        match self {
585            Self::None => {}
586            Self::Owned(i) => i.grow(),
587            Self::Borrow(i) => i.grow(),
588        }
589    }
590
591    pub fn decrease(&mut self) {
592        match self {
593            Self::None => {}
594            Self::Owned(i) => i.shrink(),
595            Self::Borrow(i) => i.shrink(),
596        }
597    }
598
599    pub fn write_indent<W: std::fmt::Write>(&mut self, mut writer: W) -> Result<(), SeError> {
600        match self {
601            Self::None => {}
602            Self::Owned(i) => {
603                writer.write_char('\n')?;
604                writer.write_str(from_utf8(i.current())?)?;
605            }
606            Self::Borrow(i) => {
607                writer.write_char('\n')?;
608                writer.write_str(from_utf8(i.current())?)?;
609            }
610        }
611        Ok(())
612    }
613}
614
615////////////////////////////////////////////////////////////////////////////////////////////////////
616
617/// A Serializer.
618///
619/// Returns the classification of the last written type.
620pub struct Serializer<'w, 'r, W: Write> {
621    ser: ContentSerializer<'w, 'r, W>,
622    /// Name of the root tag. If not specified, deduced from the structure name
623    root_tag: Option<XmlName<'r>>,
624}
625
626impl<'w, 'r, W: Write> Serializer<'w, 'r, W> {
627    /// Creates a new `Serializer` that uses struct name as a root tag name.
628    ///
629    /// Note, that attempt to serialize a non-struct (including unit structs
630    /// and newtype structs) will end up to an error. Use `with_root` to create
631    /// serializer with explicitly defined root element name
632    pub fn new(writer: &'w mut W) -> Self {
633        Self {
634            ser: ContentSerializer {
635                writer,
636                level: QuoteLevel::Partial,
637                indent: Indent::None,
638                write_indent: false,
639                text_format: TextFormat::Text,
640                allow_primitive: true,
641                empty_element_handling: EmptyElementHandling::SelfClosed,
642            },
643            root_tag: None,
644        }
645    }
646
647    /// Creates a new `Serializer` that uses specified root tag name. `name` should
648    /// be valid [XML name], otherwise error is returned.
649    ///
650    /// # Examples
651    ///
652    /// When serializing a primitive type, only its representation will be written:
653    ///
654    /// ```
655    /// # use serde::Serialize;
656    /// # use oxixml_xml::se::Serializer;
657    ///
658    /// let mut buffer = String::new();
659    /// let ser = Serializer::with_root(&mut buffer, Some("root")).expect("serialization should succeed");
660    ///
661    /// "node".serialize(ser).expect("serialization should succeed");
662    /// assert_eq!(buffer, "<root>node</root>");
663    /// ```
664    ///
665    /// When serializing a struct, newtype struct, unit struct or tuple `root_tag`
666    /// is used as tag name of root(s) element(s):
667    ///
668    /// ```
669    /// # use serde::Serialize;
670    /// # use oxixml_xml::se::Serializer;
671    ///
672    /// #[derive(Debug, PartialEq, Serialize)]
673    /// struct Struct {
674    ///     question: String,
675    ///     answer: u32,
676    /// }
677    ///
678    /// let mut buffer = String::new();
679    /// let ser = Serializer::with_root(&mut buffer, Some("root")).expect("serialization should succeed");
680    ///
681    /// let data = Struct {
682    ///     question: "The Ultimate Question of Life, the Universe, and Everything".into(),
683    ///     answer: 42,
684    /// };
685    ///
686    /// data.serialize(ser).expect("serialization should succeed");
687    /// assert_eq!(
688    ///     buffer,
689    ///     "<root>\
690    ///         <question>The Ultimate Question of Life, the Universe, and Everything</question>\
691    ///         <answer>42</answer>\
692    ///      </root>"
693    /// );
694    /// ```
695    ///
696    /// [XML name]: https://www.w3.org/TR/xml11/#NT-Name
697    pub fn with_root(writer: &'w mut W, root_tag: Option<&'r str>) -> Result<Self, SeError> {
698        Ok(Self {
699            ser: ContentSerializer {
700                writer,
701                level: QuoteLevel::Partial,
702                indent: Indent::None,
703                write_indent: false,
704                text_format: TextFormat::Text,
705                allow_primitive: true,
706                empty_element_handling: EmptyElementHandling::SelfClosed,
707            },
708            root_tag: root_tag.map(XmlName::try_from).transpose()?,
709        })
710    }
711
712    /// Enable or disable expansion of empty elements. Defaults to [`EmptyElementHandling::SelfClosed`].
713    ///
714    /// # Examples
715    ///
716    /// ```
717    /// # use serde::Serialize;
718    /// # use oxixml_xml::se::{Serializer, EmptyElementHandling};
719    /// #
720    /// #[derive(Debug, PartialEq, Serialize)]
721    /// struct Struct {
722    ///     question: Option<String>,
723    /// }
724    ///
725    /// let data = Struct {
726    ///     question: None,
727    /// };
728    ///
729    /// {
730    ///     let mut buffer = String::new();
731    ///     let mut ser = Serializer::new(&mut buffer);
732    ///     ser.empty_element_handling(EmptyElementHandling::SelfClosed);
733    ///     data.serialize(ser).expect("serialization should succeed");
734    ///     assert_eq!(
735    ///         buffer,
736    ///         "<Struct><question/></Struct>"
737    ///     );
738    /// }
739    ///
740    /// {
741    ///     let mut buffer = String::new();
742    ///     let mut ser = Serializer::new(&mut buffer);
743    ///     ser.empty_element_handling(EmptyElementHandling::SelfClosedWithSpace);
744    ///     data.serialize(ser).expect("serialization should succeed");
745    ///     assert_eq!(
746    ///         buffer,
747    ///         "<Struct><question /></Struct>"
748    ///     );
749    /// }
750    ///
751    /// {
752    ///     let mut buffer = String::new();
753    ///     let mut ser = Serializer::new(&mut buffer);
754    ///     ser.empty_element_handling(EmptyElementHandling::Expanded);
755    ///     data.serialize(ser).expect("serialization should succeed");
756    ///     assert_eq!(
757    ///         buffer,
758    ///         "<Struct><question></question></Struct>"
759    ///     );
760    /// }
761    /// ```
762    pub fn empty_element_handling(&mut self, handling: EmptyElementHandling) -> &mut Self {
763        self.ser.empty_element_handling = handling;
764        self
765    }
766
767    /// Enable or disable expansion of empty elements (without adding space before `/>`).
768    ///
769    /// This is the historycally first way to configure empty element handling. You can use
770    /// [`empty_element_handling`](Self::empty_element_handling) for more control.
771    ///
772    /// # Examples
773    ///
774    /// ```
775    /// # use serde::Serialize;
776    /// # use oxixml_xml::se::Serializer;
777    /// #
778    /// #[derive(Debug, PartialEq, Serialize)]
779    /// struct Struct {
780    ///     question: Option<String>,
781    /// }
782    ///
783    /// let mut buffer = String::new();
784    /// let mut ser = Serializer::new(&mut buffer);
785    /// ser.expand_empty_elements(true);
786    ///
787    /// let data = Struct {
788    ///     question: None,
789    /// };
790    ///
791    /// data.serialize(ser).expect("serialization should succeed");
792    /// assert_eq!(
793    ///     buffer,
794    ///     "<Struct><question></question></Struct>"
795    /// );
796    /// ```
797    pub fn expand_empty_elements(&mut self, expand: bool) -> &mut Self {
798        self.empty_element_handling(if expand {
799            EmptyElementHandling::Expanded
800        } else {
801            EmptyElementHandling::SelfClosed
802        })
803    }
804
805    /// Set the text format used for serializing text content.
806    ///
807    /// - [`TextFormat::Text`]: Regular XML escaping (default)
808    /// - [`TextFormat::CData`]: CDATA sections for text content
809    ///
810    /// # Examples
811    ///
812    /// ```
813    /// # use serde::Serialize;
814    /// # use oxixml_xml::se::{Serializer, TextFormat};
815    ///
816    /// #[derive(Debug, PartialEq, Serialize)]
817    /// struct Document {
818    ///     #[serde(rename = "$text")]
819    ///     content: String,
820    /// }
821    ///
822    /// let mut buffer = String::new();
823    /// let mut ser = Serializer::with_root(&mut buffer, Some("doc")).expect("serialization should succeed");
824    /// ser.text_format(TextFormat::CData);
825    ///
826    /// let data = Document {
827    ///     content: "Content with <markup> & entities".to_string(),
828    /// };
829    ///
830    /// data.serialize(ser).expect("serialization should succeed");
831    /// assert_eq!(buffer, "<doc><![CDATA[Content with <markup> & entities]]></doc>");
832    /// ```
833    pub fn text_format(&mut self, format: TextFormat) -> &mut Self {
834        self.ser.text_format = format;
835        self
836    }
837
838    /// Configure indent for a serializer
839    pub fn indent(&mut self, indent_char: char, indent_size: usize) -> &mut Self {
840        self.ser.indent = Indent::Owned(Indentation::new(indent_char as u8, indent_size));
841        self
842    }
843
844    /// Set the level of quoting used when writing texts
845    ///
846    /// Default: [`QuoteLevel::Partial`]
847    pub fn set_quote_level(&mut self, level: QuoteLevel) -> &mut Self {
848        self.ser.level = level;
849        self
850    }
851
852    /// Set the indent object for a serializer
853    pub(crate) fn set_indent(&mut self, indent: Indent<'r>) -> &mut Self {
854        self.ser.indent = indent;
855        self
856    }
857
858    /// Creates actual serializer or returns an error if root tag is not defined.
859    /// In that case `err` contains the name of type that cannot be serialized.
860    fn ser(self, err: &str) -> Result<ElementSerializer<'w, 'r, W>, SeError> {
861        if let Some(key) = self.root_tag {
862            Ok(ElementSerializer { ser: self.ser, key })
863        } else {
864            Err(SeError::Unsupported(
865                format!("cannot serialize {} without defined root tag", err).into(),
866            ))
867        }
868    }
869
870    /// Creates actual serializer using root tag or a specified `key` if root tag
871    /// is not defined. Returns an error if root tag is not defined and a `key`
872    /// does not conform [XML rules](XmlName::try_from) for names.
873    fn ser_name(self, key: &'static str) -> Result<ElementSerializer<'w, 'r, W>, SeError> {
874        Ok(ElementSerializer {
875            ser: self.ser,
876            key: match self.root_tag {
877                Some(key) => key,
878                None => XmlName::try_from(key)?,
879            },
880        })
881    }
882}
883
884impl<'w, 'r, W: Write> ser::Serializer for Serializer<'w, 'r, W> {
885    type Ok = WriteResult;
886    type Error = SeError;
887
888    type SerializeSeq = ElementSerializer<'w, 'r, W>;
889    type SerializeTuple = ElementSerializer<'w, 'r, W>;
890    type SerializeTupleStruct = ElementSerializer<'w, 'r, W>;
891    type SerializeTupleVariant = Tuple<'w, 'r, W>;
892    type SerializeMap = Map<'w, 'r, W>;
893    type SerializeStruct = Struct<'w, 'r, W>;
894    type SerializeStructVariant = Struct<'w, 'r, W>;
895
896    forward!(serialize_bool(bool));
897
898    forward!(serialize_i8(i8));
899    forward!(serialize_i16(i16));
900    forward!(serialize_i32(i32));
901    forward!(serialize_i64(i64));
902
903    forward!(serialize_u8(u8));
904    forward!(serialize_u16(u16));
905    forward!(serialize_u32(u32));
906    forward!(serialize_u64(u64));
907
908    forward!(serialize_i128(i128));
909    forward!(serialize_u128(u128));
910
911    forward!(serialize_f32(f32));
912    forward!(serialize_f64(f64));
913
914    forward!(serialize_char(char));
915    forward!(serialize_str(&str));
916    forward!(serialize_bytes(&[u8]));
917
918    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
919        // Do not write indent after `Option` field with `None` value, because
920        // this can be `Option<String>`. Unfortunately, we do not known what the
921        // type the option contains, so have no chance to adapt our behavior to it.
922        // The safe variant is not to write indent
923        Ok(WriteResult::SensitiveNothing)
924    }
925
926    fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, Self::Error> {
927        value.serialize(self)
928    }
929
930    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
931        self.ser("`()`")?.serialize_unit()
932    }
933
934    fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
935        self.ser_name(name)?.serialize_unit_struct(name)
936    }
937
938    fn serialize_unit_variant(
939        self,
940        name: &'static str,
941        _variant_index: u32,
942        variant: &'static str,
943    ) -> Result<Self::Ok, Self::Error> {
944        if variant == TEXT_KEY {
945            // We should write some text but we don't known what text to write
946            Err(SeError::Unsupported(
947                format!(
948                    "cannot serialize enum unit variant `{}::$text` as text content value",
949                    name
950                )
951                .into(),
952            ))
953        } else {
954            let name = XmlName::try_from(variant)?;
955            self.ser.write_empty(name)
956        }
957    }
958
959    fn serialize_newtype_struct<T: ?Sized + Serialize>(
960        self,
961        name: &'static str,
962        value: &T,
963    ) -> Result<Self::Ok, Self::Error> {
964        self.ser_name(name)?.serialize_newtype_struct(name, value)
965    }
966
967    fn serialize_newtype_variant<T: ?Sized + Serialize>(
968        self,
969        _name: &'static str,
970        _variant_index: u32,
971        variant: &'static str,
972        value: &T,
973    ) -> Result<Self::Ok, Self::Error> {
974        if variant == TEXT_KEY {
975            value.serialize(self.ser.into_simple_type_serializer()?)?;
976            // Do not write indent after `$text` variant because it may be interpreted as
977            // part of content when deserialize
978            Ok(WriteResult::SensitiveText)
979        } else {
980            let ser = ElementSerializer {
981                ser: self.ser,
982                key: XmlName::try_from(variant)?,
983            };
984            value.serialize(ser)
985        }
986    }
987
988    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
989        self.ser("sequence")?.serialize_seq(len)
990    }
991
992    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
993        self.ser("unnamed tuple")?.serialize_tuple(len)
994    }
995
996    fn serialize_tuple_struct(
997        self,
998        name: &'static str,
999        len: usize,
1000    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
1001        self.ser_name(name)?.serialize_tuple_struct(name, len)
1002    }
1003
1004    fn serialize_tuple_variant(
1005        self,
1006        name: &'static str,
1007        _variant_index: u32,
1008        variant: &'static str,
1009        len: usize,
1010    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
1011        if variant == TEXT_KEY {
1012            self.ser
1013                .into_simple_type_serializer()?
1014                .serialize_tuple_struct(name, len)
1015                .map(Tuple::Text)
1016        } else {
1017            let ser = ElementSerializer {
1018                ser: self.ser,
1019                key: XmlName::try_from(variant)?,
1020            };
1021            ser.serialize_tuple_struct(name, len).map(Tuple::Element)
1022        }
1023    }
1024
1025    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
1026        self.ser("map")?.serialize_map(len)
1027    }
1028
1029    fn serialize_struct(
1030        self,
1031        name: &'static str,
1032        len: usize,
1033    ) -> Result<Self::SerializeStruct, Self::Error> {
1034        self.ser_name(name)?.serialize_struct(name, len)
1035    }
1036
1037    fn serialize_struct_variant(
1038        self,
1039        name: &'static str,
1040        _variant_index: u32,
1041        variant: &'static str,
1042        len: usize,
1043    ) -> Result<Self::SerializeStructVariant, Self::Error> {
1044        if variant == TEXT_KEY {
1045            Err(SeError::Unsupported(
1046                format!(
1047                    "cannot serialize enum struct variant `{}::$text` as text content value",
1048                    name
1049                )
1050                .into(),
1051            ))
1052        } else {
1053            let ser = ElementSerializer {
1054                ser: self.ser,
1055                key: XmlName::try_from(variant)?,
1056            };
1057            ser.serialize_struct(name, len)
1058        }
1059    }
1060}
1061
1062#[cfg(test)]
1063mod quote_level {
1064    use super::*;
1065    use serde::Serialize;
1066
1067    #[derive(Debug, PartialEq, Serialize)]
1068    struct Element(&'static str);
1069
1070    #[derive(Debug, PartialEq, Serialize)]
1071    struct Example {
1072        #[serde(rename = "@attribute")]
1073        attribute: &'static str,
1074        element: Element,
1075    }
1076
1077    #[test]
1078    fn default_() {
1079        let example = Example {
1080            attribute: "special chars: &, <, >, \", '",
1081            element: Element("special chars: &, <, >, \", '"),
1082        };
1083
1084        let mut buffer = String::new();
1085        let ser = Serializer::new(&mut buffer);
1086
1087        example
1088            .serialize(ser)
1089            .expect("serialization should succeed");
1090        assert_eq!(
1091            buffer,
1092            "<Example attribute=\"special chars: &amp;, &lt;, &gt;, &quot;, '\">\
1093                <element>special chars: &amp;, &lt;, &gt;, \", '</element>\
1094            </Example>"
1095        );
1096    }
1097
1098    #[test]
1099    fn minimal() {
1100        let example = Example {
1101            attribute: "special chars: &, <, >, \", '",
1102            element: Element("special chars: &, <, >, \", '"),
1103        };
1104
1105        let mut buffer = String::new();
1106        let mut ser = Serializer::new(&mut buffer);
1107        ser.set_quote_level(QuoteLevel::Minimal);
1108
1109        example
1110            .serialize(ser)
1111            .expect("serialization should succeed");
1112        assert_eq!(
1113            buffer,
1114            "<Example attribute=\"special chars: &amp;, &lt;, >, &quot;, '\">\
1115                <element>special chars: &amp;, &lt;, >, \", '</element>\
1116            </Example>"
1117        );
1118    }
1119
1120    #[test]
1121    fn partial() {
1122        let example = Example {
1123            attribute: "special chars: &, <, >, \", '",
1124            element: Element("special chars: &, <, >, \", '"),
1125        };
1126
1127        let mut buffer = String::new();
1128        let mut ser = Serializer::new(&mut buffer);
1129        ser.set_quote_level(QuoteLevel::Partial);
1130
1131        example
1132            .serialize(ser)
1133            .expect("serialization should succeed");
1134        assert_eq!(
1135            buffer,
1136            "<Example attribute=\"special chars: &amp;, &lt;, &gt;, &quot;, '\">\
1137                <element>special chars: &amp;, &lt;, &gt;, \", '</element>\
1138            </Example>"
1139        );
1140    }
1141
1142    #[test]
1143    fn full() {
1144        let example = Example {
1145            attribute: "special chars: &, <, >, \", '",
1146            element: Element("special chars: &, <, >, \", '"),
1147        };
1148
1149        let mut buffer = String::new();
1150        let mut ser = Serializer::new(&mut buffer);
1151        ser.set_quote_level(QuoteLevel::Full);
1152
1153        example
1154            .serialize(ser)
1155            .expect("serialization should succeed");
1156        assert_eq!(
1157            buffer,
1158            "<Example attribute=\"special chars: &amp;, &lt;, &gt;, &quot;, &apos;\">\
1159                <element>special chars: &amp;, &lt;, &gt;, &quot;, &apos;</element>\
1160            </Example>"
1161        );
1162    }
1163}