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