Skip to main content

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