instant_xml/
impls.rs

1use std::borrow::Cow;
2use std::fmt;
3use std::net::IpAddr;
4use std::str;
5use std::str::FromStr;
6use std::{any::type_name, marker::PhantomData};
7
8#[cfg(feature = "chrono")]
9use chrono::{DateTime, NaiveDate, Utc};
10
11use crate::{Accumulate, Deserializer, Error, FromXml, Id, Kind, Serializer, ToXml};
12
13// Deserializer
14
15pub fn from_xml_str<T: FromStr>(
16    into: &mut Option<T>,
17    field: &'static str,
18    deserializer: &mut Deserializer<'_, '_>,
19) -> Result<(), Error> {
20    if into.is_some() {
21        return Err(Error::DuplicateValue(field));
22    }
23
24    let value = match deserializer.take_str()? {
25        Some(value) => value,
26        None => return Ok(()),
27    };
28
29    match T::from_str(value.as_ref()) {
30        Ok(value) => {
31            *into = Some(value);
32            Ok(())
33        }
34        Err(_) => Err(Error::UnexpectedValue(format!(
35            "unable to parse {} from `{value}`",
36            type_name::<T>()
37        ))),
38    }
39}
40
41struct FromXmlStr<T: FromStr>(T);
42
43impl<'xml, T: FromStr> FromXml<'xml> for FromXmlStr<T> {
44    #[inline]
45    fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
46        match field {
47            Some(field) => id == field,
48            None => false,
49        }
50    }
51
52    fn deserialize(
53        into: &mut Self::Accumulator,
54        field: &'static str,
55        deserializer: &mut Deserializer<'_, 'xml>,
56    ) -> Result<(), Error> {
57        if into.is_some() {
58            return Err(Error::DuplicateValue(field));
59        }
60
61        let value = match deserializer.take_str()? {
62            Some(value) => value,
63            None => return Ok(()),
64        };
65
66        match T::from_str(value.as_ref()) {
67            Ok(value) => {
68                *into = Some(FromXmlStr(value));
69                Ok(())
70            }
71            Err(_) => Err(Error::UnexpectedValue(format!(
72                "unable to parse {} from `{value}` for {field}",
73                type_name::<T>()
74            ))),
75        }
76    }
77
78    type Accumulator = Option<FromXmlStr<T>>;
79    const KIND: Kind = Kind::Scalar;
80}
81
82impl<'xml> FromXml<'xml> for bool {
83    #[inline]
84    fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
85        match field {
86            Some(field) => id == field,
87            None => false,
88        }
89    }
90
91    fn deserialize<'cx>(
92        into: &mut Self::Accumulator,
93        field: &'static str,
94        deserializer: &mut Deserializer<'cx, 'xml>,
95    ) -> Result<(), Error> {
96        if into.is_some() {
97            return Err(Error::DuplicateValue(field));
98        }
99
100        let value = match deserializer.take_str()? {
101            Some(value) => value,
102            None => return Ok(()),
103        };
104
105        let value = match value.as_ref() {
106            "true" | "1" => true,
107            "false" | "0" => false,
108            val => {
109                return Err(Error::UnexpectedValue(format!(
110                    "unable to parse bool from '{val}' for {field}"
111                )))
112            }
113        };
114
115        *into = Some(value);
116        Ok(())
117    }
118
119    type Accumulator = Option<bool>;
120    const KIND: Kind = Kind::Scalar;
121}
122
123// Serializer
124
125pub fn display_to_xml(
126    value: &impl fmt::Display,
127    field: Option<Id<'_>>,
128    serializer: &mut Serializer<impl fmt::Write + ?Sized>,
129) -> Result<(), Error> {
130    DisplayToXml(value).serialize(field, serializer)
131}
132
133struct DisplayToXml<'a, T: fmt::Display>(pub &'a T);
134
135impl<T> ToXml for DisplayToXml<'_, T>
136where
137    T: fmt::Display,
138{
139    fn serialize<W: fmt::Write + ?Sized>(
140        &self,
141        field: Option<Id<'_>>,
142        serializer: &mut Serializer<W>,
143    ) -> Result<(), Error> {
144        let prefix = match field {
145            Some(id) => {
146                let prefix = serializer.write_start(id.name, id.ns)?;
147                serializer.end_start()?;
148                Some((prefix, id.name))
149            }
150            None => None,
151        };
152
153        serializer.write_str(self.0)?;
154        if let Some((prefix, name)) = prefix {
155            serializer.write_close(prefix, name)?;
156        }
157
158        Ok(())
159    }
160}
161
162macro_rules! to_xml_for_number {
163    ($typ:ty) => {
164        impl ToXml for $typ {
165            fn serialize<W: fmt::Write + ?Sized>(
166                &self,
167                field: Option<Id<'_>>,
168                serializer: &mut Serializer<W>,
169            ) -> Result<(), Error> {
170                DisplayToXml(self).serialize(field, serializer)
171            }
172        }
173    };
174}
175
176macro_rules! from_xml_for_number {
177    ($typ:ty) => {
178        impl<'xml> FromXml<'xml> for $typ {
179            #[inline]
180            fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
181                match field {
182                    Some(field) => id == field,
183                    None => false,
184                }
185            }
186
187            fn deserialize<'cx>(
188                into: &mut Self::Accumulator,
189                field: &'static str,
190                deserializer: &mut Deserializer<'cx, 'xml>,
191            ) -> Result<(), Error> {
192                if into.is_some() {
193                    return Err(Error::DuplicateValue(field));
194                }
195
196                let mut value = None;
197                FromXmlStr::<Self>::deserialize(&mut value, field, deserializer)?;
198                if let Some(value) = value {
199                    *into = Some(value.0);
200                }
201
202                Ok(())
203            }
204
205            type Accumulator = Option<Self>;
206            const KIND: Kind = Kind::Scalar;
207        }
208    };
209}
210
211from_xml_for_number!(i8);
212from_xml_for_number!(i16);
213from_xml_for_number!(i32);
214from_xml_for_number!(i64);
215from_xml_for_number!(isize);
216from_xml_for_number!(u8);
217from_xml_for_number!(u16);
218from_xml_for_number!(u32);
219from_xml_for_number!(u64);
220from_xml_for_number!(usize);
221from_xml_for_number!(f32);
222from_xml_for_number!(f64);
223
224impl<'xml> FromXml<'xml> for char {
225    #[inline]
226    fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
227        match field {
228            Some(field) => id == field,
229            None => false,
230        }
231    }
232
233    fn deserialize<'cx>(
234        into: &mut Self::Accumulator,
235        field: &'static str,
236        deserializer: &mut Deserializer<'cx, 'xml>,
237    ) -> Result<(), Error> {
238        if into.is_some() {
239            return Err(Error::DuplicateValue(field));
240        }
241
242        let mut value = None;
243        FromXmlStr::<Self>::deserialize(&mut value, field, deserializer)?;
244        if let Some(value) = value {
245            *into = Some(value.0);
246        }
247
248        Ok(())
249    }
250
251    type Accumulator = Option<Self>;
252    const KIND: Kind = Kind::Scalar;
253}
254
255impl<'xml> FromXml<'xml> for String {
256    #[inline]
257    fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
258        match field {
259            Some(field) => id == field,
260            None => false,
261        }
262    }
263
264    fn deserialize<'cx>(
265        into: &mut Self::Accumulator,
266        field: &'static str,
267        deserializer: &mut Deserializer<'cx, 'xml>,
268    ) -> Result<(), Error> {
269        if into.is_some() {
270            return Err(Error::DuplicateValue(field));
271        }
272
273        *into = Some(match deserializer.take_str()? {
274            Some(value) => value.into_owned(),
275            None => String::new(),
276        });
277
278        Ok(())
279    }
280
281    type Accumulator = Option<String>;
282    const KIND: Kind = Kind::Scalar;
283}
284
285impl<'xml, 'a> FromXml<'xml> for Cow<'a, str> {
286    #[inline]
287    fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
288        match field {
289            Some(field) => id == field,
290            None => false,
291        }
292    }
293
294    fn deserialize(
295        into: &mut Self::Accumulator,
296        field: &'static str,
297        deserializer: &mut Deserializer<'_, 'xml>,
298    ) -> Result<(), Error> {
299        if into.inner.is_some() {
300            return Err(Error::DuplicateValue(field));
301        }
302
303        into.inner = Some(match deserializer.take_str()? {
304            Some(value) => value.into_owned().into(),
305            None => "".into(),
306        });
307
308        Ok(())
309    }
310
311    type Accumulator = CowStrAccumulator<'xml, 'a>;
312    const KIND: Kind = Kind::Scalar;
313}
314
315#[derive(Default)]
316pub struct CowStrAccumulator<'xml, 'a> {
317    pub(crate) inner: Option<Cow<'a, str>>,
318    marker: PhantomData<&'xml str>,
319}
320
321impl<'a> Accumulate<Cow<'a, str>> for CowStrAccumulator<'_, 'a> {
322    fn try_done(self, field: &'static str) -> Result<Cow<'a, str>, Error> {
323        match self.inner {
324            Some(inner) => Ok(inner),
325            None => Err(Error::MissingValue(field)),
326        }
327    }
328}
329
330// The `FromXml` implementation for `Cow<'a, [T]>` always builds a `Cow::Owned`:
331// it is not possible to deserialize into a `Cow::Borrowed` because there's no
332// place to store the originating slice (length only known at run-time).
333impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Cow<'_, [T]>
334where
335    [T]: ToOwned<Owned = Vec<T>>,
336{
337    #[inline]
338    fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
339        T::matches(id, field)
340    }
341
342    fn deserialize(
343        into: &mut Self::Accumulator,
344        field: &'static str,
345        deserializer: &mut Deserializer<'_, 'xml>,
346    ) -> Result<(), Error> {
347        let mut value = T::Accumulator::default();
348        T::deserialize(&mut value, field, deserializer)?;
349        into.push(value.try_done(field)?);
350        Ok(())
351    }
352
353    type Accumulator = Vec<T>;
354    const KIND: Kind = Kind::Scalar;
355}
356
357impl<T: ToXml> ToXml for Cow<'_, [T]>
358where
359    [T]: ToOwned,
360{
361    fn serialize<W: fmt::Write + ?Sized>(
362        &self,
363        field: Option<Id<'_>>,
364        serializer: &mut Serializer<W>,
365    ) -> Result<(), Error> {
366        self.as_ref().serialize(field, serializer)
367    }
368}
369
370impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Option<T> {
371    #[inline]
372    fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
373        T::matches(id, field)
374    }
375
376    fn deserialize<'cx>(
377        into: &mut Self::Accumulator,
378        field: &'static str,
379        deserializer: &mut Deserializer<'cx, 'xml>,
380    ) -> Result<(), Error> {
381        <T>::deserialize(&mut into.value, field, deserializer)?;
382        Ok(())
383    }
384
385    type Accumulator = OptionAccumulator<T, T::Accumulator>;
386    const KIND: Kind = <T>::KIND;
387}
388
389pub struct OptionAccumulator<T, A: Accumulate<T>> {
390    value: A,
391    marker: PhantomData<T>,
392}
393
394impl<T, A: Accumulate<T>> OptionAccumulator<T, A> {
395    pub fn get_mut(&mut self) -> &mut A {
396        &mut self.value
397    }
398}
399
400impl<T, A: Accumulate<T>> Default for OptionAccumulator<T, A> {
401    fn default() -> Self {
402        Self {
403            value: A::default(),
404            marker: PhantomData,
405        }
406    }
407}
408
409impl<T, A: Accumulate<T>> Accumulate<Option<T>> for OptionAccumulator<T, A> {
410    fn try_done(self, field: &'static str) -> Result<Option<T>, Error> {
411        match self.value.try_done(field) {
412            Ok(value) => Ok(Some(value)),
413            Err(_) => Ok(None),
414        }
415    }
416}
417
418to_xml_for_number!(i8);
419to_xml_for_number!(i16);
420to_xml_for_number!(i32);
421to_xml_for_number!(i64);
422to_xml_for_number!(isize);
423to_xml_for_number!(u8);
424to_xml_for_number!(u16);
425to_xml_for_number!(u32);
426to_xml_for_number!(u64);
427to_xml_for_number!(usize);
428to_xml_for_number!(f32);
429to_xml_for_number!(f64);
430
431impl ToXml for bool {
432    fn serialize<W: fmt::Write + ?Sized>(
433        &self,
434        field: Option<Id<'_>>,
435        serializer: &mut Serializer<W>,
436    ) -> Result<(), Error> {
437        let value = match self {
438            true => "true",
439            false => "false",
440        };
441
442        DisplayToXml(&value).serialize(field, serializer)
443    }
444}
445
446impl ToXml for String {
447    fn serialize<W: fmt::Write + ?Sized>(
448        &self,
449        field: Option<Id<'_>>,
450        serializer: &mut Serializer<W>,
451    ) -> Result<(), Error> {
452        DisplayToXml(&encode(self)?).serialize(field, serializer)
453    }
454}
455
456impl ToXml for char {
457    fn serialize<W: fmt::Write + ?Sized>(
458        &self,
459        field: Option<Id<'_>>,
460        serializer: &mut Serializer<W>,
461    ) -> Result<(), Error> {
462        let mut tmp = [0u8; 4];
463        DisplayToXml(&encode(&*self.encode_utf8(&mut tmp))?).serialize(field, serializer)
464    }
465}
466
467impl ToXml for str {
468    fn serialize<W: fmt::Write + ?Sized>(
469        &self,
470        field: Option<Id<'_>>,
471        serializer: &mut Serializer<W>,
472    ) -> Result<(), Error> {
473        DisplayToXml(&encode(self)?).serialize(field, serializer)
474    }
475}
476
477impl ToXml for Cow<'_, str> {
478    fn serialize<W: fmt::Write + ?Sized>(
479        &self,
480        field: Option<Id<'_>>,
481        serializer: &mut Serializer<W>,
482    ) -> Result<(), Error> {
483        DisplayToXml(&encode(self)?).serialize(field, serializer)
484    }
485}
486
487impl<T: ToXml> ToXml for Option<T> {
488    fn serialize<W: fmt::Write + ?Sized>(
489        &self,
490        field: Option<Id<'_>>,
491        serializer: &mut Serializer<W>,
492    ) -> Result<(), Error> {
493        match self {
494            Some(v) => v.serialize(field, serializer),
495            None => Ok(()),
496        }
497    }
498
499    fn present(&self) -> bool {
500        self.is_some()
501    }
502}
503
504impl<T: ToXml + ?Sized> ToXml for Box<T> {
505    fn serialize<W: fmt::Write + ?Sized>(
506        &self,
507        field: Option<Id<'_>>,
508        serializer: &mut Serializer<W>,
509    ) -> Result<(), Error> {
510        self.as_ref().serialize(field, serializer)
511    }
512}
513
514impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Box<T> {
515    #[inline]
516    fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
517        T::matches(id, field)
518    }
519
520    fn deserialize<'cx>(
521        into: &mut Self::Accumulator,
522        field: &'static str,
523        deserializer: &mut Deserializer<'cx, 'xml>,
524    ) -> Result<(), Error> {
525        if into.is_some() {
526            return Err(Error::DuplicateValue(field));
527        }
528
529        let mut value = T::Accumulator::default();
530        T::deserialize(&mut value, field, deserializer)?;
531        *into = Some(Box::new(value.try_done(field)?));
532
533        Ok(())
534    }
535
536    type Accumulator = Option<Self>;
537    const KIND: Kind = T::KIND;
538}
539
540fn encode(input: &str) -> Result<Cow<'_, str>, Error> {
541    let mut result = String::with_capacity(input.len());
542    let mut last_end = 0;
543    for (start, c) in input.char_indices() {
544        let to = match c {
545            '&' => "&amp;",
546            '"' => "&quot;",
547            '<' => "&lt;",
548            '>' => "&gt;",
549            '\'' => "&apos;",
550            _ => continue,
551        };
552        result.push_str(input.get(last_end..start).unwrap());
553        result.push_str(to);
554        last_end = start + 1;
555    }
556
557    if result.is_empty() {
558        return Ok(Cow::Borrowed(input));
559    }
560
561    result.push_str(input.get(last_end..input.len()).unwrap());
562    Ok(Cow::Owned(result))
563}
564
565impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Vec<T> {
566    #[inline]
567    fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
568        T::matches(id, field)
569    }
570
571    fn deserialize<'cx>(
572        into: &mut Self::Accumulator,
573        field: &'static str,
574        deserializer: &mut Deserializer<'cx, 'xml>,
575    ) -> Result<(), Error> {
576        let mut value = T::Accumulator::default();
577        T::deserialize(&mut value, field, deserializer)?;
578        into.push(value.try_done(field)?);
579        Ok(())
580    }
581
582    type Accumulator = Vec<T>;
583    const KIND: Kind = T::KIND;
584}
585
586impl<T: ToXml> ToXml for Vec<T> {
587    fn serialize<W: fmt::Write + ?Sized>(
588        &self,
589        field: Option<Id<'_>>,
590        serializer: &mut Serializer<W>,
591    ) -> Result<(), Error> {
592        self.as_slice().serialize(field, serializer)
593    }
594}
595
596impl<T: ToXml> ToXml for [T] {
597    fn serialize<W: fmt::Write + ?Sized>(
598        &self,
599        field: Option<Id<'_>>,
600        serializer: &mut Serializer<W>,
601    ) -> Result<(), Error> {
602        for i in self {
603            i.serialize(field, serializer)?;
604        }
605
606        Ok(())
607    }
608}
609
610#[cfg(feature = "chrono")]
611impl ToXml for DateTime<Utc> {
612    fn serialize<W: fmt::Write + ?Sized>(
613        &self,
614        field: Option<Id<'_>>,
615        serializer: &mut Serializer<W>,
616    ) -> Result<(), Error> {
617        let prefix = match field {
618            Some(id) => {
619                let prefix = serializer.write_start(id.name, id.ns)?;
620                serializer.end_start()?;
621                Some((prefix, id.name))
622            }
623            None => None,
624        };
625
626        serializer.write_str(&self.to_rfc3339())?;
627        if let Some((prefix, name)) = prefix {
628            serializer.write_close(prefix, name)?;
629        }
630
631        Ok(())
632    }
633}
634
635#[cfg(feature = "chrono")]
636impl<'xml> FromXml<'xml> for DateTime<Utc> {
637    #[inline]
638    fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
639        match field {
640            Some(field) => id == field,
641            None => false,
642        }
643    }
644
645    fn deserialize<'cx>(
646        into: &mut Self::Accumulator,
647        field: &'static str,
648        deserializer: &mut Deserializer<'cx, 'xml>,
649    ) -> Result<(), Error> {
650        if into.is_some() {
651            return Err(Error::DuplicateValue(field));
652        }
653
654        let value = match deserializer.take_str()? {
655            Some(value) => value,
656            None => return Ok(()),
657        };
658
659        match DateTime::parse_from_rfc3339(value.as_ref()) {
660            Ok(dt) if dt.timezone().utc_minus_local() == 0 => {
661                *into = Some(dt.with_timezone(&Utc));
662                Ok(())
663            }
664            _ => Err(Error::Other("invalid date/time".into())),
665        }
666    }
667
668    type Accumulator = Option<Self>;
669    const KIND: Kind = Kind::Scalar;
670}
671
672#[cfg(feature = "chrono")]
673impl ToXml for NaiveDate {
674    fn serialize<W: fmt::Write + ?Sized>(
675        &self,
676        field: Option<Id<'_>>,
677        serializer: &mut Serializer<W>,
678    ) -> Result<(), Error> {
679        let prefix = match field {
680            Some(id) => {
681                let prefix = serializer.write_start(id.name, id.ns)?;
682                serializer.end_start()?;
683                Some((prefix, id.name))
684            }
685            None => None,
686        };
687
688        serializer.write_str(&self)?;
689        if let Some((prefix, name)) = prefix {
690            serializer.write_close(prefix, name)?;
691        }
692
693        Ok(())
694    }
695}
696
697#[cfg(feature = "chrono")]
698impl<'xml> FromXml<'xml> for NaiveDate {
699    #[inline]
700    fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
701        match field {
702            Some(field) => id == field,
703            None => false,
704        }
705    }
706
707    fn deserialize<'cx>(
708        into: &mut Self::Accumulator,
709        field: &'static str,
710        deserializer: &mut Deserializer<'cx, 'xml>,
711    ) -> Result<(), Error> {
712        if into.is_some() {
713            return Err(Error::DuplicateValue(field));
714        }
715
716        let value = match deserializer.take_str()? {
717            Some(value) => value,
718            None => return Ok(()),
719        };
720
721        match NaiveDate::parse_from_str(value.as_ref(), "%Y-%m-%d") {
722            Ok(d) => {
723                *into = Some(d);
724                Ok(())
725            }
726            _ => Err(Error::Other("invalid date/time".into())),
727        }
728    }
729
730    type Accumulator = Option<Self>;
731    const KIND: Kind = Kind::Scalar;
732}
733
734impl<'xml> FromXml<'xml> for () {
735    #[inline]
736    fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
737        match field {
738            Some(field) => id == field,
739            None => false,
740        }
741    }
742
743    fn deserialize<'cx>(
744        into: &mut Self::Accumulator,
745        _: &'static str,
746        _: &mut Deserializer<'cx, 'xml>,
747    ) -> Result<(), Error> {
748        *into = Some(());
749        Ok(())
750    }
751
752    type Accumulator = Option<Self>;
753    const KIND: Kind = Kind::Scalar;
754}
755
756impl ToXml for IpAddr {
757    fn serialize<W: fmt::Write + ?Sized>(
758        &self,
759        field: Option<Id<'_>>,
760        serializer: &mut Serializer<W>,
761    ) -> Result<(), Error> {
762        DisplayToXml(self).serialize(field, serializer)
763    }
764}
765
766impl<'xml> FromXml<'xml> for IpAddr {
767    #[inline]
768    fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
769        match field {
770            Some(field) => id == field,
771            None => false,
772        }
773    }
774
775    fn deserialize<'cx>(
776        into: &mut Self::Accumulator,
777        field: &'static str,
778        deserializer: &mut Deserializer<'cx, 'xml>,
779    ) -> Result<(), Error> {
780        if into.is_some() {
781            return Err(Error::DuplicateValue(field));
782        }
783
784        let mut value = None;
785        FromXmlStr::<Self>::deserialize(&mut value, field, deserializer)?;
786        if let Some(value) = value {
787            *into = Some(value.0);
788        }
789
790        Ok(())
791    }
792
793    type Accumulator = Option<Self>;
794    const KIND: Kind = Kind::Scalar;
795}
796
797#[cfg(test)]
798mod tests {
799    use super::*;
800
801    #[test]
802    fn encode_unicode() {
803        let input = "Iñtërnâ&tiônàlizætiøn";
804        assert_eq!(encode(input).unwrap(), "Iñtërnâ&amp;tiônàlizætiøn");
805    }
806}