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