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 & 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 /// `<` | `<`
393 /// `>` | `>`
394 /// `&` | `&`
395 /// `"` | `"`
396 /// `'` | `'`
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 /// `<` | `<`
408 /// `>` | `>`
409 /// `&` | `&`
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 /// `<` | `<`
421 /// `&` | `&`
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: &, <, >, ", '\">\
1093 <element>special chars: &, <, >, \", '</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: &, <, >, ", '\">\
1115 <element>special chars: &, <, >, \", '</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: &, <, >, ", '\">\
1137 <element>special chars: &, <, >, \", '</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: &, <, >, ", '\">\
1159 <element>special chars: &, <, >, ", '</element>\
1160 </Example>"
1161 );
1162 }
1163}