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