Skip to main content

deser_xml/
mixed.rs

1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::fmt;
4use std::hash::{Hash, Hasher};
5use std::marker::PhantomData;
6use std::ops::{Deref, DerefMut};
7
8use deser_core::de::{Deserialize, OwnedSink, Sink, SinkHandle, default_atom};
9use deser_core::ser::SerializeRef;
10use deser_core::ser::{
11    Boxed, Describe, Emit, SerializeHandle, StructEmitter, Variant, VariantKind, VariantRepr,
12};
13use deser_core::{Atom, Error, ErrorKind, Serialize, State, Text};
14
15use crate::Names;
16
17/// The content of an element in order: its text and child elements.
18///
19/// Elements are maps whose repeated keys are collected by key: the text of
20/// `<p>x <b>y</b> z</p>` is `["x ", " z"]` for a `Vec<String>` field with
21/// the text key.  `Mixed` keeps the order instead.  Every child element
22/// and every text between them is a value of `T`, which receives it as a
23/// map with a single entry: the name of the element (or the
24/// [text key](crate::DeserializerConfig::set_text_key) for text) and its
25/// value.  This is what externally tagged enums expect:
26///
27/// ```
28/// use deser::{Deserialize, Serialize};
29/// use deser_xml::Mixed;
30///
31/// #[derive(Debug, Deserialize, Serialize, PartialEq)]
32/// enum Inline {
33///     #[deser(rename = "$text")]
34///     Text(String),
35///     #[deser(rename = "b")]
36///     Bold(String),
37/// }
38///
39/// let p: Mixed<Inline> =
40///     deser_xml::from_str("<p>x <b>y</b> z</p>").unwrap();
41/// assert_eq!(
42///     p.0,
43///     [
44///         Inline::Text("x ".into()),
45///         Inline::Bold("y".into()),
46///         Inline::Text(" z".into())
47///     ]
48/// );
49/// ```
50///
51/// Attributes are not content, they are skipped.  To read them too
52/// flatten `Mixed` into a struct: the fields of the struct take their
53/// attributes and child elements, the rest is the content in order.
54///
55/// ```
56/// # use deser::{Deserialize, Serialize};
57/// # use deser_xml::Mixed;
58/// # #[derive(Debug, Deserialize, Serialize, PartialEq)]
59/// # enum Inline {
60/// #     #[deser(rename = "$text")]
61/// #     Text(String),
62/// #     #[deser(rename = "b")]
63/// #     Bold(String),
64/// # }
65/// #[derive(Debug, Deserialize, Serialize, PartialEq)]
66/// #[deser(rename = "p")]
67/// struct Paragraph {
68///     #[deser(rename = "@class")]
69///     class: Option<String>,
70///     #[deser(flatten)]
71///     content: Mixed<Inline>,
72/// }
73///
74/// let p: Paragraph =
75///     deser_xml::from_str(r#"<p class="note">x <b>y</b></p>"#).unwrap();
76/// assert_eq!(p.class.as_deref(), Some("note"));
77/// assert_eq!(p.content.len(), 2);
78/// assert_eq!(
79///     deser_xml::to_string(&p).unwrap(),
80///     r#"<p class="note">x <b>y</b></p>"#
81/// );
82/// ```
83///
84/// Text that is only whitespace is kept within the elements whose
85/// content is `Mixed` (it's not text elsewhere): `<b>y</b> <b>z</b>` has
86/// the space between the elements.  If `Mixed` is flattened into a
87/// struct, this starts with the first value it takes.  Texts that are
88/// separated by values that are skipped or taken by the fields of the
89/// struct are separate values.  For content where whitespace is only
90/// indentation, [`SkipWhitespace`] leaves it out:
91///
92/// ```
93/// use deser::Deserialize;
94/// use deser::adapters::TrimWhitespace;
95/// use deser_xml::{Mixed, SkipWhitespace};
96///
97/// #[derive(Debug, Deserialize, PartialEq)]
98/// enum Block {
99///     #[deser(rename = "$text")]
100///     Text(#[deser(as = TrimWhitespace)] String),
101///     #[deser(rename = "p")]
102///     Paragraph(String),
103/// }
104///
105/// let doc: Mixed<Block, SkipWhitespace> = deser_xml::from_str("
106///     <doc>
107///       <p>a</p>
108///       text
109///       <p>b</p>
110///     </doc>
111/// ").unwrap();
112/// assert_eq!(doc.0, [
113///     Block::Paragraph("a".into()),
114///     Block::Text("text".into()),
115///     Block::Paragraph("b".into()),
116/// ]);
117/// ```
118///
119/// Values that are left out by `T` (that leave no value like
120/// [`SkipBlank`](deser_core::adapters::SkipBlank) does) are not content.
121///
122/// When serialized, each value becomes the entries of the element it
123/// serializes as (unit variants are empty elements).  As whitespace is
124/// text, indented output (see
125/// [`SerializerConfig::set_indent`](crate::SerializerConfig::set_indent)) writes
126/// the content on a single line, unless it's [`SkipWhitespace`].
127pub struct Mixed<T, W = KeepWhitespace>(pub Vec<T>, PhantomData<fn() -> W>);
128
129/// What [`Mixed`] does with text that is only whitespace.
130///
131/// This is [`KeepWhitespace`] or [`SkipWhitespace`].
132pub trait Whitespace: sealed::Sealed + 'static {
133    #[doc(hidden)]
134    const KEEP: bool;
135}
136
137/// [`Mixed`] keeps text that is only whitespace (the default).
138#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
139pub struct KeepWhitespace;
140
141/// [`Mixed`] leaves out text that is only whitespace.
142///
143/// Whitespace between child elements is not text, as it's not for other
144/// types than `Mixed`.  An element that is only whitespace has no
145/// content.
146#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
147pub struct SkipWhitespace;
148
149impl Whitespace for KeepWhitespace {
150    const KEEP: bool = true;
151}
152
153impl Whitespace for SkipWhitespace {
154    const KEEP: bool = false;
155}
156
157mod sealed {
158    pub trait Sealed {}
159    impl Sealed for super::KeepWhitespace {}
160    impl Sealed for super::SkipWhitespace {}
161}
162
163impl<T, W> Mixed<T, W> {
164    /// Creates empty content.
165    pub const fn new() -> Mixed<T, W> {
166        Mixed(Vec::new(), PhantomData)
167    }
168
169    /// Returns the values.
170    pub fn into_inner(self) -> Vec<T> {
171        self.0
172    }
173}
174
175impl<T: fmt::Debug, W> fmt::Debug for Mixed<T, W> {
176    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177        f.debug_tuple("Mixed").field(&self.0).finish()
178    }
179}
180
181impl<T: Clone, W> Clone for Mixed<T, W> {
182    fn clone(&self) -> Mixed<T, W> {
183        Mixed(self.0.clone(), PhantomData)
184    }
185}
186
187impl<T: PartialEq, W> PartialEq for Mixed<T, W> {
188    fn eq(&self, other: &Mixed<T, W>) -> bool {
189        self.0 == other.0
190    }
191}
192
193impl<T: Eq, W> Eq for Mixed<T, W> {}
194
195impl<T: PartialOrd, W> PartialOrd for Mixed<T, W> {
196    fn partial_cmp(&self, other: &Mixed<T, W>) -> Option<Ordering> {
197        self.0.partial_cmp(&other.0)
198    }
199}
200
201impl<T: Ord, W> Ord for Mixed<T, W> {
202    fn cmp(&self, other: &Mixed<T, W>) -> Ordering {
203        self.0.cmp(&other.0)
204    }
205}
206
207impl<T: Hash, W> Hash for Mixed<T, W> {
208    fn hash<H: Hasher>(&self, state: &mut H) {
209        self.0.hash(state)
210    }
211}
212
213impl<T, W> Default for Mixed<T, W> {
214    fn default() -> Mixed<T, W> {
215        Mixed::new()
216    }
217}
218
219impl<T, W> Deref for Mixed<T, W> {
220    type Target = Vec<T>;
221
222    fn deref(&self) -> &Vec<T> {
223        &self.0
224    }
225}
226
227impl<T, W> DerefMut for Mixed<T, W> {
228    fn deref_mut(&mut self) -> &mut Vec<T> {
229        &mut self.0
230    }
231}
232
233impl<T, W> From<Vec<T>> for Mixed<T, W> {
234    fn from(values: Vec<T>) -> Mixed<T, W> {
235        Mixed(values, PhantomData)
236    }
237}
238
239impl<T, W> FromIterator<T> for Mixed<T, W> {
240    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Mixed<T, W> {
241        Mixed(iter.into_iter().collect(), PhantomData)
242    }
243}
244
245impl<T, W> IntoIterator for Mixed<T, W> {
246    type Item = T;
247    type IntoIter = std::vec::IntoIter<T>;
248
249    fn into_iter(self) -> Self::IntoIter {
250        self.0.into_iter()
251    }
252}
253
254impl<'a, T, W> IntoIterator for &'a Mixed<T, W> {
255    type Item = &'a T;
256    type IntoIter = std::slice::Iter<'a, T>;
257
258    fn into_iter(self) -> Self::IntoIter {
259        self.0.iter()
260    }
261}
262
263/// The depths of the elements whose whitespace is text.
264///
265/// Whitespace between child elements is not text, unless the content of
266/// the element is [`Mixed`] (with [`KeepWhitespace`]).  Its sink registers
267/// the depth within the element here, the parser checks it before it
268/// drops whitespace.
269#[derive(Debug, Default)]
270pub(crate) struct WhitespaceDepths(pub(crate) Vec<usize>);
271
272impl WhitespaceDepths {
273    /// Returns `true` if whitespace is text at the current depth.
274    pub(crate) fn applies(state: &State) -> bool {
275        state
276            .get::<WhitespaceDepths>()
277            .and_then(|keep| keep.0.last())
278            .is_some_and(|&depth| depth == state.depth())
279    }
280
281    /// Forgets the elements that were closed.
282    ///
283    /// Sinks unregister themselves when they finish, but sinks that are
284    /// dropped (after errors or as flattened fields without values) do
285    /// not.
286    pub(crate) fn prune(state: &mut State) {
287        let depth = state.depth();
288        if state.get::<WhitespaceDepths>().is_some() {
289            state
290                .get_mut::<WhitespaceDepths>()
291                .0
292                .retain(|&keep| keep <= depth);
293        }
294    }
295}
296
297impl<'de, T: Deserialize<'de>, W: Whitespace> Deserialize<'de> for Mixed<T, W> {
298    fn deserialize_into<'out>(
299        out: &'out mut Option<Self>,
300        state: &mut State,
301    ) -> SinkHandle<'out, 'de> {
302        SinkHandle::arena(
303            MixedSink {
304                out,
305                values: Vec::new(),
306                key: None,
307                pending: None,
308                depth: None,
309            },
310            state,
311        )
312    }
313
314    fn expecting() -> Cow<'static, str> {
315        Cow::Borrowed("mixed content")
316    }
317
318    /// Missing content is empty.
319    fn initial_value() -> Option<Self> {
320        Some(Mixed::new())
321    }
322}
323
324struct MixedSink<'a, 'de, T, W> {
325    out: &'a mut Option<Mixed<T, W>>,
326    values: Vec<T>,
327    key: Option<String>,
328    /// The value that receives the value of the last key.
329    pending: Option<OwnedSink<'de, T>>,
330    /// The depth registered in [`WhitespaceDepths`].
331    depth: Option<usize>,
332}
333
334impl<'de, T: Deserialize<'de>, W: Whitespace> MixedSink<'_, 'de, T, W> {
335    /// Keeps whitespace in the element at the depth.
336    fn keep_whitespace(&mut self, depth: usize, state: &mut State) {
337        if W::KEEP && self.depth.is_none() {
338            self.depth = Some(depth);
339            state.get_mut::<WhitespaceDepths>().0.push(depth);
340        }
341    }
342
343    /// Returns `true` if the text is content.
344    fn is_content(text: &str) -> bool {
345        if W::KEEP {
346            !text.is_empty()
347        } else {
348            !text.trim().is_empty()
349        }
350    }
351
352    /// Begins a value with the key and returns the sink of its value.
353    fn begin(&mut self, key: &str, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
354        self.end(state)?;
355        let pending = self.pending.insert(OwnedSink::deserialize(state));
356        let sink = pending.get_mut();
357        sink.map(state)?;
358        let mut key_sink = sink.next_key(state)?;
359        key_sink.atom(Atom::Lexical(Text::borrowed(key)), state)?;
360        key_sink.finish(state)?;
361        drop(key_sink);
362        sink.next_value(state)
363    }
364
365    /// Ends the pending value.
366    ///
367    /// Values that leave no value are left out.
368    fn end(&mut self, state: &mut State) -> Result<(), Error> {
369        if let Some(mut pending) = self.pending.take() {
370            pending.get_mut().finish(state)?;
371            self.values.extend(pending.take());
372        }
373        Ok(())
374    }
375}
376
377impl<'de, T: Deserialize<'de>, W: Whitespace> Sink<'de> for MixedSink<'_, 'de, T, W> {
378    /// Text is an element without attributes and child elements, empty
379    /// text (and blank text with [`SkipWhitespace`]) has no content.
380    fn atom(&mut self, atom: Atom, state: &mut State) -> Result<(), Error> {
381        match atom {
382            Atom::Null => Ok(()),
383            Atom::Str(ref text) | Atom::Lexical(ref text) if !Self::is_content(text) => Ok(()),
384            Atom::Str(_) | Atom::Lexical(_) => {
385                let mut sink = self.begin(text_key(state), state)?;
386                sink.atom(atom, state)?;
387                sink.finish(state)
388            }
389            atom => default_atom(self, atom, state),
390        }
391    }
392
393    fn borrowed_atom(&mut self, atom: Atom<'de>, state: &mut State) -> Result<(), Error> {
394        match atom {
395            Atom::Str(ref text) | Atom::Lexical(ref text) if Self::is_content(text) => {
396                let mut sink = self.begin(text_key(state), state)?;
397                sink.borrowed_atom(atom, state)?;
398                sink.finish(state)
399            }
400            atom => self.atom(atom, state),
401        }
402    }
403
404    fn map(&mut self, state: &mut State) -> Result<(), Error> {
405        // the depth is increased after this
406        self.keep_whitespace(state.depth() + 1, state);
407        Ok(())
408    }
409
410    fn next_key(&mut self, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
411        self.end(state)?;
412        Ok(String::deserialize_into(&mut self.key, state))
413    }
414
415    fn next_value(&mut self, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
416        let key = self.key.take().unwrap_or_default();
417        Ok(self
418            .value_for_key(&key, state)?
419            .unwrap_or_else(SinkHandle::null))
420    }
421
422    /// Takes all keys but attributes when flattened into a struct.
423    fn value_for_key(
424        &mut self,
425        key: &str,
426        state: &mut State,
427    ) -> Result<Option<SinkHandle<'_, 'de>>, Error> {
428        let prefix = names(state).attribute_prefix;
429        if !prefix.is_empty() && key.starts_with(prefix) {
430            return Ok(None);
431        }
432        // within the map of the element
433        self.keep_whitespace(state.depth(), state);
434        self.begin(key, state).map(Some)
435    }
436
437    fn finish(&mut self, state: &mut State) -> Result<(), Error> {
438        self.end(state)?;
439        if let Some(depth) = self.depth.take() {
440            let keep = &mut state.get_mut::<WhitespaceDepths>().0;
441            if keep.last() == Some(&depth) {
442                keep.pop();
443            }
444        }
445        *self.out = Some(Mixed(std::mem::take(&mut self.values), PhantomData));
446        Ok(())
447    }
448
449    fn recover(&mut self, err: Error, state: &mut State) -> Result<(), Error> {
450        self.key = None;
451        match self.pending {
452            Some(ref mut pending) => pending.get_mut().recover(err, state),
453            None => Err(err),
454        }
455    }
456
457    fn expecting(&self) -> Cow<'_, str> {
458        Cow::Borrowed("mixed content")
459    }
460}
461
462fn names(state: &State) -> &Names {
463    const DEFAULT: Names = Names::new();
464    state.get::<Names>().unwrap_or(&DEFAULT)
465}
466
467fn text_key(state: &State) -> &'static str {
468    names(state).text_key
469}
470
471/// Marks content that keeps whitespace as text, the serializer does not
472/// indent it.
473///
474/// This is event data of the start of the map or, if the content is
475/// flattened, of its first key.
476#[derive(Debug, Default, Clone)]
477pub(crate) struct KeepsWhitespace(pub(crate) bool);
478
479impl<T: Serialize, W: Whitespace> Serialize for Mixed<T, W> {
480    fn serialize<'a>(value: &'a Self, state: &mut State) -> Result<Emit<'a>, Error> {
481        if W::KEEP {
482            state.event_mut::<KeepsWhitespace>().0 = true;
483        }
484        Ok(Emit::structure(
485            MixedEmitter {
486                values: value.0.iter(),
487                current: None,
488            },
489            state,
490        ))
491    }
492}
493
494/// The entries of the value that is serialized.
495enum Entries<'a> {
496    Struct(Boxed<dyn StructEmitter + 'a>),
497    /// A unit variant, an empty element.
498    Unit(Option<Cow<'a, str>>),
499}
500
501struct MixedEmitter<'a, T> {
502    values: std::slice::Iter<'a, T>,
503    current: Option<(&'a T, Entries<'a>)>,
504}
505
506impl<'a, T: Serialize> StructEmitter for MixedEmitter<'a, T> {
507    fn next(
508        &mut self,
509        state: &mut State,
510    ) -> Result<Option<(Cow<'_, str>, SerializeHandle<'_>)>, Error> {
511        loop {
512            if let Some((_, ref mut entries)) = self.current {
513                let entry = match entries {
514                    Entries::Struct(emitter) => emitter.next(state)?,
515                    Entries::Unit(name) => name.take().map(|name| (name, SerializeHandle::to(&""))),
516                };
517                // SAFETY: the entry borrows from `self.current`.  If it's
518                // returned `self.current` is not touched again in this call,
519                // otherwise it's dropped before `self.current` is replaced.
520                // The borrow checker does not understand that the borrow
521                // does not continue into the next iteration.
522                let entry = unsafe {
523                    std::mem::transmute::<
524                        Option<(Cow<'_, str>, SerializeHandle<'_>)>,
525                        Option<(Cow<'a, str>, SerializeHandle<'a>)>,
526                    >(entry)
527                };
528                if let Some(entry) = entry {
529                    return Ok(Some(entry));
530                }
531                let (value, entries) = self.current.take().unwrap();
532                drop(entries);
533                T::finish(value, state)?;
534            }
535            let Some(value) = self.values.next() else {
536                return Ok(None);
537            };
538            let entries = match T::serialize(value, state)? {
539                Emit::Struct(emitter) => Entries::Struct(emitter),
540                Emit::Atom(Atom::Str(name) | Atom::Lexical(name))
541                    if is_unit_variant(SerializeRef::new(value)) =>
542                {
543                    Entries::Unit(Some(name.into_cow()))
544                }
545                Emit::Atom(Atom::Null) => Entries::Unit(None),
546                _ => {
547                    return Err(Error::new(
548                        ErrorKind::UnsupportedType,
549                        "the values of mixed content must be structs or externally tagged enums",
550                    ));
551                }
552            };
553            self.current = Some((value, entries));
554        }
555    }
556}
557
558/// Returns `true` if the value is a unit variant that is its name.
559fn is_unit_variant(value: SerializeRef<'_>) -> bool {
560    struct Check(bool);
561
562    impl Describe for Check {
563        fn variant(&mut self, variant: &Variant<'_>) {
564            self.0 = variant.kind == VariantKind::Unit && variant.repr == VariantRepr::External;
565        }
566    }
567
568    let mut check = Check(false);
569    value.describe(&mut check);
570    check.0
571}