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
13pub 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
123pub 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
338impl<'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 '&' => "&",
554 '"' => """,
555 '<' => "<",
556 '>' => ">",
557 '\'' => "'",
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â&tiônàlizætiøn");
875 }
876}