1use crate::escape::escape_char;
7use crate::se::{QuoteLevel, SeError};
8use crate::utils::CDataIterator;
9use serde::ser::{
10 Impossible, Serialize, SerializeSeq, SerializeTuple, SerializeTupleStruct,
11 SerializeTupleVariant, Serializer,
12};
13use std::fmt::{self, Write};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum QuoteTarget {
17 Text,
19 DoubleQAttr,
21 SingleQAttr,
23 CData,
26}
27
28fn escape_into<W, F>(mut writer: W, value: &str, escape_chars: F) -> fmt::Result
29where
30 W: Write,
31 F: Fn(u8) -> bool,
32{
33 let bytes = value.as_bytes();
34 let mut iter = bytes.iter();
35 let mut pos = 0;
36 while let Some(i) = iter.position(|&b| escape_chars(b)) {
37 let new_pos = pos + i;
38 escape_char(&mut writer, value, pos, new_pos)?;
39 pos = new_pos + 1;
40 }
41
42 if let Some(raw) = value.get(pos..) {
43 writer.write_str(raw)?;
44 }
45 Ok(())
46}
47
48fn escape_item<W>(mut writer: W, value: &str, target: QuoteTarget, level: QuoteLevel) -> fmt::Result
51where
52 W: Write,
53{
54 use QuoteLevel::*;
55 use QuoteTarget::*;
56
57 match (target, level) {
58 (CData, _) => {
59 let mut it = CDataIterator::new(value);
60 if let Some(part) = it.next() {
61 writer.write_str(part)?;
62 }
63 for part in it {
64 writer.write_str("]]><![CDATA[")?;
65 writer.write_str(part)?;
66 }
67 Ok(())
68 }
69 (_, Full) => escape_into(writer, value, |ch| match ch {
70 b' ' | b'\r' | b'\n' | b'\t' => true,
72 b'&' | b'<' | b'>' | b'\'' | b'\"' => true,
74 _ => false,
75 }),
76 (Text, Partial) => escape_into(writer, value, |ch| match ch {
78 b' ' | b'\r' | b'\n' | b'\t' => true,
80 b'&' | b'<' | b'>' => true,
82 _ => false,
83 }),
84 (Text, Minimal) => escape_into(writer, value, |ch| match ch {
85 b' ' | b'\r' | b'\n' | b'\t' => true,
87 b'&' | b'<' => true,
89 _ => false,
90 }),
91 (DoubleQAttr, Partial) => escape_into(writer, value, |ch| match ch {
93 b' ' | b'\r' | b'\n' | b'\t' => true,
95 b'&' | b'<' | b'>' => true,
97 b'"' => true,
99 _ => false,
100 }),
101 (DoubleQAttr, Minimal) => escape_into(writer, value, |ch| match ch {
102 b' ' | b'\r' | b'\n' | b'\t' => true,
104 b'&' | b'<' => true,
106 b'"' => true,
108 _ => false,
109 }),
110 (SingleQAttr, Partial) => escape_into(writer, value, |ch| match ch {
112 b' ' | b'\r' | b'\n' | b'\t' => true,
114 b'&' | b'<' | b'>' => true,
116 b'\'' => true,
118 _ => false,
119 }),
120 (SingleQAttr, Minimal) => escape_into(writer, value, |ch| match ch {
121 b' ' | b'\r' | b'\n' | b'\t' => true,
123 b'&' | b'<' => true,
125 b'\'' => true,
127 _ => false,
128 }),
129 }
130}
131
132fn escape_list<W>(mut writer: W, value: &str, target: QuoteTarget, level: QuoteLevel) -> fmt::Result
140where
141 W: Write,
142{
143 use QuoteLevel::*;
144 use QuoteTarget::*;
145
146 match (target, level) {
147 (CData, _) => {
148 for part in CDataIterator::new(value) {
149 writer.write_str("<![CDATA[")?;
150 writer.write_str(part)?;
151 writer.write_str("]]>")?;
152 }
153 Ok(())
154 }
155 (Text, Full) => escape_into(writer, value, |ch| match ch {
156 b'\r' => true,
158 b'&' | b'<' | b'>' | b'\'' | b'\"' => true,
160 _ => false,
161 }),
162 (Text, Partial) => escape_into(writer, value, |ch| match ch {
163 b'\r' => true,
165 b'&' | b'<' | b'>' => true,
167 _ => false,
168 }),
169 (Text, Minimal) => escape_into(writer, value, |ch| match ch {
170 b'\r' => true,
172 b'&' | b'<' => true,
174 _ => false,
175 }),
176 (_, Full) => escape_into(writer, value, |ch| match ch {
178 b'\r' | b'\n' | b'\t' => true,
181 b'&' | b'<' | b'>' | b'\'' | b'\"' => true,
183 _ => false,
184 }),
185 (DoubleQAttr, Partial) => escape_into(writer, value, |ch| match ch {
187 b'\r' | b'\n' | b'\t' => true,
189 b'&' | b'<' | b'>' => true,
191 b'"' => true,
193 _ => false,
194 }),
195 (DoubleQAttr, Minimal) => escape_into(writer, value, |ch| match ch {
196 b'\r' | b'\n' | b'\t' => true,
198 b'&' | b'<' => true,
200 b'"' => true,
202 _ => false,
203 }),
204 (SingleQAttr, Partial) => escape_into(writer, value, |ch| match ch {
206 b'\r' | b'\n' | b'\t' => true,
208 b'&' | b'<' | b'>' => true,
210 b'\'' => true,
212 _ => false,
213 }),
214 (SingleQAttr, Minimal) => escape_into(writer, value, |ch| match ch {
215 b'\r' | b'\n' | b'\t' => true,
217 b'&' | b'<' => true,
219 b'\'' => true,
221 _ => false,
222 }),
223 }
224}
225
226macro_rules! write_atomic {
229 ($method:ident ( $ty:ty )) => {
230 fn $method(mut self, value: $ty) -> Result<Self::Ok, Self::Error> {
231 self.write_fmt(format_args!("{}", value))?;
232 Ok(true)
233 }
234 };
235}
236
237pub struct AtomicSerializer<W: Write> {
258 pub writer: W,
259 pub target: QuoteTarget,
260 pub level: QuoteLevel,
262 pub(crate) write_delimiter: bool,
264}
265
266impl<W: Write> AtomicSerializer<W> {
267 fn write_delimiter(&mut self) -> fmt::Result {
268 if self.write_delimiter {
269 return self.writer.write_char(' ');
271 }
272 Ok(())
273 }
274 fn write_str(&mut self, value: &str) -> Result<(), SeError> {
275 self.write_delimiter()?;
276 Ok(self.writer.write_str(value)?)
277 }
278 fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), SeError> {
279 self.write_delimiter()?;
280 Ok(self.writer.write_fmt(args)?)
281 }
282}
283
284impl<W: Write> Serializer for AtomicSerializer<W> {
285 type Ok = bool;
286 type Error = SeError;
287
288 type SerializeSeq = Impossible<Self::Ok, Self::Error>;
289 type SerializeTuple = Impossible<Self::Ok, Self::Error>;
290 type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
291 type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
292 type SerializeMap = Impossible<Self::Ok, Self::Error>;
293 type SerializeStruct = Impossible<Self::Ok, Self::Error>;
294 type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
295
296 fn serialize_bool(mut self, value: bool) -> Result<Self::Ok, Self::Error> {
297 self.write_str(if value { "true" } else { "false" })?;
298 Ok(true)
299 }
300
301 write_atomic!(serialize_i8(i8));
302 write_atomic!(serialize_i16(i16));
303 write_atomic!(serialize_i32(i32));
304 write_atomic!(serialize_i64(i64));
305
306 write_atomic!(serialize_u8(u8));
307 write_atomic!(serialize_u16(u16));
308 write_atomic!(serialize_u32(u32));
309 write_atomic!(serialize_u64(u64));
310
311 write_atomic!(serialize_i128(i128));
312 write_atomic!(serialize_u128(u128));
313
314 write_atomic!(serialize_f32(f32));
315 write_atomic!(serialize_f64(f64));
316
317 fn serialize_char(self, value: char) -> Result<Self::Ok, Self::Error> {
318 self.serialize_str(value.encode_utf8(&mut [0u8; 4]))
319 }
320
321 fn serialize_str(mut self, value: &str) -> Result<Self::Ok, Self::Error> {
322 if !value.is_empty() {
323 self.write_delimiter()?;
324 escape_item(self.writer, value, self.target, self.level)?;
325 }
326 Ok(!value.is_empty())
327 }
328
329 fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, Self::Error> {
330 Err(SeError::Unsupported(
332 "`serialize_bytes` not supported yet".into(),
333 ))
334 }
335
336 fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
337 Ok(false)
338 }
339
340 fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, Self::Error> {
341 value.serialize(self)
342 }
343
344 fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
347 Err(SeError::Unsupported(
348 "cannot serialize unit type `()` as an `xs:list` item".into(),
349 ))
350 }
351
352 fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
355 Err(SeError::Unsupported(
356 format!(
357 "cannot serialize unit struct `{}` as an `xs:list` item",
358 name
359 )
360 .into(),
361 ))
362 }
363
364 fn serialize_unit_variant(
365 self,
366 _name: &'static str,
367 _variant_index: u32,
368 variant: &'static str,
369 ) -> Result<Self::Ok, Self::Error> {
370 self.serialize_str(variant)
371 }
372
373 fn serialize_newtype_struct<T: ?Sized + Serialize>(
374 self,
375 _name: &'static str,
376 value: &T,
377 ) -> Result<Self::Ok, Self::Error> {
378 value.serialize(self)
379 }
380
381 fn serialize_newtype_variant<T: ?Sized + Serialize>(
384 self,
385 name: &'static str,
386 _variant_index: u32,
387 variant: &'static str,
388 _value: &T,
389 ) -> Result<Self::Ok, SeError> {
390 Err(SeError::Unsupported(
391 format!(
392 "cannot serialize enum newtype variant `{}::{}` as an `xs:list` item",
393 name, variant
394 )
395 .into(),
396 ))
397 }
398
399 fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
400 Err(SeError::Unsupported(
401 "cannot serialize sequence as an `xs:list` item".into(),
402 ))
403 }
404
405 fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
406 Err(SeError::Unsupported(
407 "cannot serialize tuple as an `xs:list` item".into(),
408 ))
409 }
410
411 fn serialize_tuple_struct(
412 self,
413 name: &'static str,
414 _len: usize,
415 ) -> Result<Self::SerializeTupleStruct, Self::Error> {
416 Err(SeError::Unsupported(
417 format!(
418 "cannot serialize tuple struct `{}` as an `xs:list` item",
419 name
420 )
421 .into(),
422 ))
423 }
424
425 fn serialize_tuple_variant(
426 self,
427 name: &'static str,
428 _variant_index: u32,
429 variant: &'static str,
430 _len: usize,
431 ) -> Result<Self::SerializeTupleVariant, Self::Error> {
432 Err(SeError::Unsupported(
433 format!(
434 "cannot serialize enum tuple variant `{}::{}` as an `xs:list` item",
435 name, variant
436 )
437 .into(),
438 ))
439 }
440
441 fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
442 Err(SeError::Unsupported(
443 "cannot serialize map as an `xs:list` item".into(),
444 ))
445 }
446
447 fn serialize_struct(
448 self,
449 name: &'static str,
450 _len: usize,
451 ) -> Result<Self::SerializeStruct, Self::Error> {
452 Err(SeError::Unsupported(
453 format!("cannot serialize struct `{}` as an `xs:list` item", name).into(),
454 ))
455 }
456
457 fn serialize_struct_variant(
458 self,
459 name: &'static str,
460 _variant_index: u32,
461 variant: &'static str,
462 _len: usize,
463 ) -> Result<Self::SerializeStructVariant, Self::Error> {
464 Err(SeError::Unsupported(
465 format!(
466 "cannot serialize enum struct variant `{}::{}` as an `xs:list` item",
467 name, variant
468 )
469 .into(),
470 ))
471 }
472}
473
474pub struct SimpleTypeSerializer<W: Write> {
483 pub writer: W,
485 pub target: QuoteTarget,
487 pub level: QuoteLevel,
489}
490
491impl<W: Write> SimpleTypeSerializer<W> {
492 #[inline]
493 fn write_str(&mut self, value: &str) -> Result<(), SeError> {
494 Ok(self.writer.write_str(value)?)
495 }
496 #[inline]
497 fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), SeError> {
498 Ok(self.writer.write_fmt(args)?)
499 }
500}
501
502impl<W: Write> Serializer for SimpleTypeSerializer<W> {
503 type Ok = W;
504 type Error = SeError;
505
506 type SerializeSeq = SimpleSeq<W>;
507 type SerializeTuple = SimpleSeq<W>;
508 type SerializeTupleStruct = SimpleSeq<W>;
509 type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
510 type SerializeMap = Impossible<Self::Ok, Self::Error>;
511 type SerializeStruct = Impossible<Self::Ok, Self::Error>;
512 type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
513
514 write_primitive!();
515
516 fn serialize_str(mut self, value: &str) -> Result<Self::Ok, Self::Error> {
517 if !value.is_empty() {
518 escape_list(&mut self.writer, value, self.target, self.level)?;
519 }
520 Ok(self.writer)
521 }
522
523 fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
525 Ok(self.writer)
526 }
527
528 fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
530 Ok(self.writer)
531 }
532
533 fn serialize_newtype_variant<T: ?Sized + Serialize>(
536 self,
537 name: &'static str,
538 _variant_index: u32,
539 variant: &'static str,
540 _value: &T,
541 ) -> Result<Self::Ok, SeError> {
542 Err(SeError::Unsupported(
543 format!("cannot serialize enum newtype variant `{}::{}` as an attribute or text content value", name, variant).into(),
544 ))
545 }
546
547 #[inline]
548 fn serialize_seq(mut self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
549 if let QuoteTarget::CData = self.target {
550 self.writer.write_str("<![CDATA[")?;
551 }
552 Ok(SimpleSeq {
553 writer: self.writer,
554 target: self.target,
555 level: self.level,
556 is_empty: true,
557 })
558 }
559
560 #[inline]
561 fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
562 self.serialize_seq(None)
563 }
564
565 #[inline]
566 fn serialize_tuple_struct(
567 self,
568 _name: &'static str,
569 _len: usize,
570 ) -> Result<Self::SerializeTupleStruct, Self::Error> {
571 self.serialize_seq(None)
572 }
573
574 fn serialize_tuple_variant(
575 self,
576 name: &'static str,
577 _variant_index: u32,
578 variant: &'static str,
579 _len: usize,
580 ) -> Result<Self::SerializeTupleVariant, Self::Error> {
581 Err(SeError::Unsupported(
582 format!("cannot serialize enum tuple variant `{}::{}` as an attribute or text content value", name, variant).into(),
583 ))
584 }
585
586 fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
587 Err(SeError::Unsupported(
588 "cannot serialize map as an attribute or text content value".into(),
589 ))
590 }
591
592 fn serialize_struct(
593 self,
594 name: &'static str,
595 _len: usize,
596 ) -> Result<Self::SerializeStruct, Self::Error> {
597 Err(SeError::Unsupported(
598 format!(
599 "cannot serialize struct `{}` as an attribute or text content value",
600 name
601 )
602 .into(),
603 ))
604 }
605
606 fn serialize_struct_variant(
607 self,
608 name: &'static str,
609 _variant_index: u32,
610 variant: &'static str,
611 _len: usize,
612 ) -> Result<Self::SerializeStructVariant, Self::Error> {
613 Err(SeError::Unsupported(
614 format!("cannot serialize enum struct variant `{}::{}` as an attribute or text content value", name, variant).into(),
615 ))
616 }
617}
618
619pub struct SimpleSeq<W: Write> {
621 writer: W,
622 target: QuoteTarget,
623 level: QuoteLevel,
624 is_empty: bool,
626}
627
628impl<W: Write> SerializeSeq for SimpleSeq<W> {
629 type Ok = W;
630 type Error = SeError;
631
632 fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
633 where
634 T: ?Sized + Serialize,
635 {
636 if value.serialize(AtomicSerializer {
637 writer: &mut self.writer,
638 target: self.target,
639 level: self.level,
640 write_delimiter: !self.is_empty,
641 })? {
642 self.is_empty = false;
643 }
644 Ok(())
645 }
646
647 #[inline]
648 fn end(mut self) -> Result<Self::Ok, Self::Error> {
649 if let QuoteTarget::CData = self.target {
650 self.writer.write_str("]]>")?;
651 }
652 Ok(self.writer)
653 }
654}
655
656impl<W: Write> SerializeTuple for SimpleSeq<W> {
657 type Ok = W;
658 type Error = SeError;
659
660 #[inline]
661 fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
662 where
663 T: ?Sized + Serialize,
664 {
665 SerializeSeq::serialize_element(self, value)
666 }
667
668 #[inline]
669 fn end(self) -> Result<Self::Ok, Self::Error> {
670 SerializeSeq::end(self)
671 }
672}
673
674impl<W: Write> SerializeTupleStruct for SimpleSeq<W> {
675 type Ok = W;
676 type Error = SeError;
677
678 #[inline]
679 fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
680 where
681 T: ?Sized + Serialize,
682 {
683 SerializeSeq::serialize_element(self, value)
684 }
685
686 #[inline]
687 fn end(self) -> Result<Self::Ok, Self::Error> {
688 SerializeSeq::end(self)
689 }
690}
691
692impl<W: Write> SerializeTupleVariant for SimpleSeq<W> {
693 type Ok = W;
694 type Error = SeError;
695
696 #[inline]
697 fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
698 where
699 T: ?Sized + Serialize,
700 {
701 SerializeSeq::serialize_element(self, value)
702 }
703
704 #[inline]
705 fn end(self) -> Result<Self::Ok, Self::Error> {
706 SerializeSeq::end(self)
707 }
708}
709
710#[cfg(test)]
713mod tests {
714 use super::*;
715 use crate::utils::Bytes;
716 use serde::Serialize;
717 use std::collections::BTreeMap;
718
719 #[derive(Debug, Serialize, PartialEq)]
720 struct Unit;
721
722 #[derive(Debug, Serialize, PartialEq)]
723 struct Newtype(usize);
724
725 #[derive(Debug, Serialize, PartialEq)]
726 struct Tuple(&'static str, usize);
727
728 #[derive(Debug, Serialize, PartialEq)]
729 struct Struct {
730 key: &'static str,
731 val: usize,
732 }
733
734 #[derive(Debug, Serialize, PartialEq)]
735 enum Enum {
736 Unit,
737 #[serde(rename = "<\"&'>")]
738 UnitEscaped,
739 Newtype(usize),
740 Tuple(&'static str, usize),
741 Struct {
742 key: &'static str,
743 val: usize,
744 },
745 }
746
747 mod escape_item {
748 use super::*;
749 use pretty_assertions::assert_eq;
750
751 fn escape_item(value: &str, target: QuoteTarget, level: QuoteLevel) -> String {
752 let mut result = String::new();
753 super::escape_item(&mut result, value, target, level).unwrap();
754 result
755 }
756
757 mod full {
758 use super::*;
759 use pretty_assertions::assert_eq;
760
761 #[test]
762 fn text() {
763 assert_eq!(
764 escape_item("text<\"'&> \t\n\rtext", QuoteTarget::Text, QuoteLevel::Full),
765 "text<"'&> 	 text"
766 );
767 }
768
769 #[test]
770 fn double_quote_attr() {
771 assert_eq!(
772 escape_item(
773 "text<\"'&> \t\n\rtext",
774 QuoteTarget::DoubleQAttr,
775 QuoteLevel::Full
776 ),
777 "text<"'&> 	 text"
778 );
779 }
780
781 #[test]
782 fn single_quote_attr() {
783 assert_eq!(
784 escape_item(
785 "text<\"'&> \t\n\rtext",
786 QuoteTarget::SingleQAttr,
787 QuoteLevel::Full
788 ),
789 "text<"'&> 	 text"
790 );
791 }
792 }
793
794 mod partial {
795 use super::*;
796 use pretty_assertions::assert_eq;
797
798 #[test]
799 fn text() {
800 assert_eq!(
801 escape_item(
802 "text<\"'&> \t\n\rtext",
803 QuoteTarget::Text,
804 QuoteLevel::Partial
805 ),
806 "text<\"'&> 	 text"
807 );
808 }
809
810 #[test]
811 fn double_quote_attr() {
812 assert_eq!(
813 escape_item(
814 "text<\"'&> \t\n\rtext",
815 QuoteTarget::DoubleQAttr,
816 QuoteLevel::Partial
817 ),
818 "text<"'&> 	 text"
819 );
820 }
821
822 #[test]
823 fn single_quote_attr() {
824 assert_eq!(
825 escape_item(
826 "text<\"'&> \t\n\rtext",
827 QuoteTarget::SingleQAttr,
828 QuoteLevel::Partial
829 ),
830 "text<\"'&> 	 text"
831 );
832 }
833 }
834
835 mod minimal {
836 use super::*;
837 use pretty_assertions::assert_eq;
838
839 #[test]
840 fn text() {
841 assert_eq!(
842 escape_item(
843 "text<\"'&> \t\n\rtext",
844 QuoteTarget::Text,
845 QuoteLevel::Minimal
846 ),
847 "text<\"'&> 	 text"
848 );
849 }
850
851 #[test]
852 fn double_quote_attr() {
853 assert_eq!(
854 escape_item(
855 "text<\"'&> \t\n\rtext",
856 QuoteTarget::DoubleQAttr,
857 QuoteLevel::Minimal
858 ),
859 "text<"'&> 	 text"
860 );
861 }
862
863 #[test]
864 fn single_quote_attr() {
865 assert_eq!(
866 escape_item(
867 "text<\"'&> \t\n\rtext",
868 QuoteTarget::SingleQAttr,
869 QuoteLevel::Minimal
870 ),
871 "text<\"'&> 	 text"
872 );
873 }
874 }
875
876 #[test]
878 fn cdata() {
879 assert_eq!(
880 escape_item(
881 "text<\"'&>]]> \t\n\rtext",
882 QuoteTarget::CData,
883 QuoteLevel::Full
884 ),
885 "text<\"'&>]]]]><![CDATA[> \t\n\rtext"
886 );
887 assert_eq!(
888 escape_item(
889 "text<\"'&>]]> \t\n\rtext",
890 QuoteTarget::CData,
891 QuoteLevel::Partial
892 ),
893 "text<\"'&>]]]]><![CDATA[> \t\n\rtext"
894 );
895 assert_eq!(
896 escape_item(
897 "text<\"'&>]]> \t\n\rtext",
898 QuoteTarget::CData,
899 QuoteLevel::Minimal
900 ),
901 "text<\"'&>]]]]><![CDATA[> \t\n\rtext"
902 );
903 }
904 }
905
906 mod escape_list {
907 use super::*;
908 use pretty_assertions::assert_eq;
909
910 fn escape_list(value: &str, target: QuoteTarget, level: QuoteLevel) -> String {
911 let mut result = String::new();
912 super::escape_list(&mut result, value, target, level).unwrap();
913 result
914 }
915
916 mod full {
917 use super::*;
918 use pretty_assertions::assert_eq;
919
920 #[test]
921 fn text() {
922 assert_eq!(
923 escape_list("text<\"'&> \t\n\rtext", QuoteTarget::Text, QuoteLevel::Full),
924 "text<"'&> \t\n text"
925 );
926 }
927
928 #[test]
929 fn double_quote_attr() {
930 assert_eq!(
931 escape_list(
932 "text<\"'&> \t\n\rtext",
933 QuoteTarget::DoubleQAttr,
934 QuoteLevel::Full
935 ),
936 "text<"'&> 	 text"
937 );
938 }
939
940 #[test]
941 fn single_quote_attr() {
942 assert_eq!(
943 escape_list(
944 "text<\"'&> \t\n\rtext",
945 QuoteTarget::SingleQAttr,
946 QuoteLevel::Full
947 ),
948 "text<"'&> 	 text"
949 );
950 }
951 }
952
953 mod partial {
954 use super::*;
955 use pretty_assertions::assert_eq;
956
957 #[test]
958 fn text() {
959 assert_eq!(
960 escape_list(
961 "text<\"'&> \t\n\rtext",
962 QuoteTarget::Text,
963 QuoteLevel::Partial
964 ),
965 "text<\"'&> \t\n text"
966 );
967 }
968
969 #[test]
970 fn double_quote_attr() {
971 assert_eq!(
972 escape_list(
973 "text<\"'&> \t\n\rtext",
974 QuoteTarget::DoubleQAttr,
975 QuoteLevel::Partial
976 ),
977 "text<"'&> 	 text"
978 );
979 }
980
981 #[test]
982 fn single_quote_attr() {
983 assert_eq!(
984 escape_list(
985 "text<\"'&> \t\n\rtext",
986 QuoteTarget::SingleQAttr,
987 QuoteLevel::Partial
988 ),
989 "text<\"'&> 	 text"
990 );
991 }
992 }
993
994 mod minimal {
995 use super::*;
996 use pretty_assertions::assert_eq;
997
998 #[test]
999 fn text() {
1000 assert_eq!(
1001 escape_list(
1002 "text<\"'&> \t\n\rtext",
1003 QuoteTarget::Text,
1004 QuoteLevel::Minimal
1005 ),
1006 "text<\"'&> \t\n text"
1007 );
1008 }
1009
1010 #[test]
1011 fn double_quote_attr() {
1012 assert_eq!(
1013 escape_list(
1014 "text<\"'&> \t\n\rtext",
1015 QuoteTarget::DoubleQAttr,
1016 QuoteLevel::Minimal
1017 ),
1018 "text<"'&> 	 text"
1019 );
1020 }
1021
1022 #[test]
1023 fn single_quote_attr() {
1024 assert_eq!(
1025 escape_list(
1026 "text<\"'&> \t\n\rtext",
1027 QuoteTarget::SingleQAttr,
1028 QuoteLevel::Minimal
1029 ),
1030 "text<\"'&> 	 text"
1031 );
1032 }
1033 }
1034
1035 #[test]
1036 fn cdata() {
1037 assert_eq!(
1038 escape_list(
1039 "text<\"'&>]]> \t\n\rtext",
1040 QuoteTarget::CData,
1041 QuoteLevel::Full
1042 ),
1043 "<![CDATA[text<\"'&>]]]]><![CDATA[> \t\n\rtext]]>"
1044 );
1045 assert_eq!(
1046 escape_list(
1047 "text<\"'&>]]> \t\n\rtext",
1048 QuoteTarget::CData,
1049 QuoteLevel::Partial
1050 ),
1051 "<![CDATA[text<\"'&>]]]]><![CDATA[> \t\n\rtext]]>"
1052 );
1053 assert_eq!(
1054 escape_list(
1055 "text<\"'&>]]> \t\n\rtext",
1056 QuoteTarget::CData,
1057 QuoteLevel::Minimal
1058 ),
1059 "<![CDATA[text<\"'&>]]]]><![CDATA[> \t\n\rtext]]>"
1060 );
1061 }
1062 }
1063
1064 mod atomic {
1066 use super::*;
1067 use pretty_assertions::assert_eq;
1068
1069 macro_rules! serialize_as {
1071 ($name:ident: $data:expr => $expected:literal) => {
1072 #[test]
1073 fn $name() {
1074 let mut buffer = String::new();
1075 let ser = AtomicSerializer {
1076 writer: &mut buffer,
1077 target: QuoteTarget::Text,
1078 level: QuoteLevel::Full,
1079 write_delimiter: false,
1080 };
1081
1082 let has_written = $data.serialize(ser).unwrap();
1083 assert_eq!(buffer, $expected);
1084 assert_eq!(has_written, !buffer.is_empty());
1085 }
1086 };
1087 }
1088
1089 macro_rules! err {
1092 ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
1093 #[test]
1094 fn $name() {
1095 let mut buffer = String::new();
1096 let ser = AtomicSerializer {
1097 writer: &mut buffer,
1098 target: QuoteTarget::Text,
1099 level: QuoteLevel::Full,
1100 write_delimiter: false,
1101 };
1102
1103 match $data.serialize(ser).unwrap_err() {
1104 SeError::$kind(e) => assert_eq!(e, $reason),
1105 e => panic!(
1106 "Expected `Err({}({}))`, but got `{:?}`",
1107 stringify!($kind),
1108 $reason,
1109 e
1110 ),
1111 }
1112 assert_eq!(buffer, "");
1113 }
1114 };
1115 }
1116
1117 serialize_as!(false_: false => "false");
1118 serialize_as!(true_: true => "true");
1119
1120 serialize_as!(i8_: -42i8 => "-42");
1121 serialize_as!(i16_: -4200i16 => "-4200");
1122 serialize_as!(i32_: -42000000i32 => "-42000000");
1123 serialize_as!(i64_: -42000000000000i64 => "-42000000000000");
1124 serialize_as!(isize_: -42000000isize => "-42000000");
1125
1126 serialize_as!(u8_: 42u8 => "42");
1127 serialize_as!(u16_: 4200u16 => "4200");
1128 serialize_as!(u32_: 42000000u32 => "42000000");
1129 serialize_as!(u64_: 42000000000000u64 => "42000000000000");
1130 serialize_as!(usize_: 42000000usize => "42000000");
1131
1132 serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
1133 serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000");
1134
1135 serialize_as!(f32_: 4.2f32 => "4.2");
1136 serialize_as!(f64_: 4.2f64 => "4.2");
1137
1138 serialize_as!(char_non_escaped: 'h' => "h");
1139 serialize_as!(char_lt: '<' => "<");
1140 serialize_as!(char_gt: '>' => ">");
1141 serialize_as!(char_amp: '&' => "&");
1142 serialize_as!(char_apos: '\'' => "'");
1143 serialize_as!(char_quot: '"' => """);
1144
1145 serialize_as!(str_non_escaped: "non-escaped-string" => "non-escaped-string");
1146 serialize_as!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>");
1147
1148 err!(bytes: Bytes(b"<\"escaped & bytes'>")
1149 => Unsupported("`serialize_bytes` not supported yet"));
1150
1151 serialize_as!(option_none: Option::<&str>::None => "");
1152 serialize_as!(option_some: Some("non-escaped-string") => "non-escaped-string");
1153
1154 err!(unit: ()
1155 => Unsupported("cannot serialize unit type `()` as an `xs:list` item"));
1156 err!(unit_struct: Unit
1157 => Unsupported("cannot serialize unit struct `Unit` as an `xs:list` item"));
1158
1159 serialize_as!(enum_unit: Enum::Unit => "Unit");
1160 serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>");
1161
1162 serialize_as!(newtype: Newtype(42) => "42");
1163 err!(enum_newtype: Enum::Newtype(42)
1164 => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an `xs:list` item"));
1165
1166 err!(seq: vec![1, 2, 3]
1167 => Unsupported("cannot serialize sequence as an `xs:list` item"));
1168 err!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize)
1169 => Unsupported("cannot serialize tuple as an `xs:list` item"));
1170 err!(tuple_struct: Tuple("first", 42)
1171 => Unsupported("cannot serialize tuple struct `Tuple` as an `xs:list` item"));
1172 err!(enum_tuple: Enum::Tuple("first", 42)
1173 => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an `xs:list` item"));
1174
1175 err!(map: BTreeMap::from([(1, 2), (3, 4)])
1176 => Unsupported("cannot serialize map as an `xs:list` item"));
1177 err!(struct_: Struct { key: "answer", val: 42 }
1178 => Unsupported("cannot serialize struct `Struct` as an `xs:list` item"));
1179 err!(enum_struct: Enum::Struct { key: "answer", val: 42 }
1180 => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an `xs:list` item"));
1181 }
1182
1183 mod simple_type {
1184 use super::*;
1185 use pretty_assertions::assert_eq;
1186
1187 macro_rules! serialize_as {
1189 ($name:ident: $data:expr => $expected:literal) => {
1190 #[test]
1191 fn $name() {
1192 let ser = SimpleTypeSerializer {
1193 writer: String::new(),
1194 target: QuoteTarget::Text,
1195 level: QuoteLevel::Full,
1196 };
1197
1198 let buffer = $data.serialize(ser).unwrap();
1199 assert_eq!(buffer, $expected);
1200 }
1201 };
1202 }
1203
1204 macro_rules! err {
1207 ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
1208 #[test]
1209 fn $name() {
1210 let mut buffer = String::new();
1211 let ser = SimpleTypeSerializer {
1212 writer: &mut buffer,
1213 target: QuoteTarget::Text,
1214 level: QuoteLevel::Full,
1215 };
1216
1217 match $data.serialize(ser).unwrap_err() {
1218 SeError::$kind(e) => assert_eq!(e, $reason),
1219 e => panic!(
1220 "Expected `Err({}({}))`, but got `{:?}`",
1221 stringify!($kind),
1222 $reason,
1223 e
1224 ),
1225 }
1226 assert_eq!(buffer, "");
1227 }
1228 };
1229 }
1230
1231 serialize_as!(false_: false => "false");
1232 serialize_as!(true_: true => "true");
1233
1234 serialize_as!(i8_: -42i8 => "-42");
1235 serialize_as!(i16_: -4200i16 => "-4200");
1236 serialize_as!(i32_: -42000000i32 => "-42000000");
1237 serialize_as!(i64_: -42000000000000i64 => "-42000000000000");
1238 serialize_as!(isize_: -42000000isize => "-42000000");
1239
1240 serialize_as!(u8_: 42u8 => "42");
1241 serialize_as!(u16_: 4200u16 => "4200");
1242 serialize_as!(u32_: 42000000u32 => "42000000");
1243 serialize_as!(u64_: 42000000000000u64 => "42000000000000");
1244 serialize_as!(usize_: 42000000usize => "42000000");
1245
1246 serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
1247 serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000");
1248
1249 serialize_as!(f32_: 4.2f32 => "4.2");
1250 serialize_as!(f64_: 4.2f64 => "4.2");
1251
1252 serialize_as!(char_non_escaped: 'h' => "h");
1253 serialize_as!(char_lt: '<' => "<");
1254 serialize_as!(char_gt: '>' => ">");
1255 serialize_as!(char_amp: '&' => "&");
1256 serialize_as!(char_apos: '\'' => "'");
1257 serialize_as!(char_quot: '"' => """);
1258
1259 serialize_as!(str_non_escaped: "non-escaped string" => "non-escaped string");
1260 serialize_as!(str_escaped: "<\"escaped & string'>" => "<"escaped & string'>");
1261
1262 err!(bytes: Bytes(b"<\"escaped & bytes'>")
1263 => Unsupported("`serialize_bytes` not supported yet"));
1264
1265 serialize_as!(option_none: Option::<&str>::None => "");
1266 serialize_as!(option_some: Some("non-escaped string") => "non-escaped string");
1267
1268 serialize_as!(unit: () => "");
1269 serialize_as!(unit_struct: Unit => "");
1270
1271 serialize_as!(enum_unit: Enum::Unit => "Unit");
1272 serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>");
1273
1274 serialize_as!(newtype: Newtype(42) => "42");
1275 err!(enum_newtype: Enum::Newtype(42)
1276 => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an attribute or text content value"));
1277
1278 serialize_as!(seq: vec![1, 2, 3] => "1 2 3");
1279 serialize_as!(seq_empty: Vec::<usize>::new() => "");
1280 serialize_as!(seq_with_1_empty_str: vec![""] => "");
1281 serialize_as!(seq_with_2_empty_strs: vec!["", ""] => "");
1282 serialize_as!(tuple: ("<\"&'>", "with\t\n\r spaces", 3usize)
1283 => "<"&'> with	  spaces 3");
1284 serialize_as!(tuple_struct: Tuple("first", 42) => "first 42");
1285 err!(enum_tuple: Enum::Tuple("first", 42)
1286 => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an attribute or text content value"));
1287
1288 err!(map: BTreeMap::from([(1, 2), (3, 4)])
1289 => Unsupported("cannot serialize map as an attribute or text content value"));
1290 err!(struct_: Struct { key: "answer", val: 42 }
1291 => Unsupported("cannot serialize struct `Struct` as an attribute or text content value"));
1292 err!(enum_struct: Enum::Struct { key: "answer", val: 42 }
1293 => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value"));
1294 }
1295
1296 mod simple_seq {
1297 use super::*;
1298 use pretty_assertions::assert_eq;
1299
1300 #[test]
1301 fn empty_seq() {
1302 let mut buffer = String::new();
1303 let ser = SimpleSeq {
1304 writer: &mut buffer,
1305 target: QuoteTarget::Text,
1306 level: QuoteLevel::Full,
1307 is_empty: true,
1308 };
1309
1310 SerializeSeq::end(ser).unwrap();
1311 assert_eq!(buffer, "");
1312 }
1313
1314 #[test]
1315 fn all_items_empty() {
1316 let mut buffer = String::new();
1317 let mut ser = SimpleSeq {
1318 writer: &mut buffer,
1319 target: QuoteTarget::Text,
1320 level: QuoteLevel::Full,
1321 is_empty: true,
1322 };
1323
1324 SerializeSeq::serialize_element(&mut ser, "").unwrap();
1325 SerializeSeq::serialize_element(&mut ser, "").unwrap();
1326 SerializeSeq::serialize_element(&mut ser, "").unwrap();
1327 SerializeSeq::end(ser).unwrap();
1328 assert_eq!(buffer, "");
1329 }
1330
1331 #[test]
1332 fn some_items_empty1() {
1333 let mut buffer = String::new();
1334 let mut ser = SimpleSeq {
1335 writer: &mut buffer,
1336 target: QuoteTarget::Text,
1337 level: QuoteLevel::Full,
1338 is_empty: true,
1339 };
1340
1341 SerializeSeq::serialize_element(&mut ser, "").unwrap();
1342 SerializeSeq::serialize_element(&mut ser, &1).unwrap();
1343 SerializeSeq::serialize_element(&mut ser, "").unwrap();
1344 SerializeSeq::end(ser).unwrap();
1345 assert_eq!(buffer, "1");
1346 }
1347
1348 #[test]
1349 fn some_items_empty2() {
1350 let mut buffer = String::new();
1351 let mut ser = SimpleSeq {
1352 writer: &mut buffer,
1353 target: QuoteTarget::Text,
1354 level: QuoteLevel::Full,
1355 is_empty: true,
1356 };
1357
1358 SerializeSeq::serialize_element(&mut ser, &1).unwrap();
1359 SerializeSeq::serialize_element(&mut ser, "").unwrap();
1360 SerializeSeq::serialize_element(&mut ser, &2).unwrap();
1361 SerializeSeq::end(ser).unwrap();
1362 assert_eq!(buffer, "1 2");
1363 }
1364
1365 #[test]
1366 fn items() {
1367 let mut buffer = String::new();
1368 let mut ser = SimpleSeq {
1369 writer: &mut buffer,
1370 target: QuoteTarget::Text,
1371 level: QuoteLevel::Full,
1372 is_empty: true,
1373 };
1374
1375 SerializeSeq::serialize_element(&mut ser, &1).unwrap();
1376 SerializeSeq::serialize_element(&mut ser, &2).unwrap();
1377 SerializeSeq::serialize_element(&mut ser, &3).unwrap();
1378 SerializeSeq::end(ser).unwrap();
1379 assert_eq!(buffer, "1 2 3");
1380 }
1381 }
1382
1383 mod cdata {
1384 use super::*;
1385 use pretty_assertions::assert_eq;
1386
1387 macro_rules! serialize_as_cdata {
1388 ($name:ident: $data:expr => $expected:literal) => {
1389 #[test]
1390 fn $name() {
1391 let ser = SimpleTypeSerializer {
1392 writer: String::new(),
1393 target: QuoteTarget::CData,
1394 level: QuoteLevel::Full,
1395 };
1396
1397 let buffer = $data.serialize(ser).unwrap();
1398 assert_eq!(buffer, $expected);
1399 }
1400 };
1401 }
1402
1403 serialize_as_cdata!(empty_string: "" => "");
1404 serialize_as_cdata!(simple_text: "Hello World" => "<![CDATA[Hello World]]>");
1405 serialize_as_cdata!(with_markup: "<tag>content</tag>" => "<![CDATA[<tag>content</tag>]]>");
1406 serialize_as_cdata!(with_ampersand: "Tom & Jerry" => "<![CDATA[Tom & Jerry]]>");
1407 serialize_as_cdata!(with_quotes: r#"He said "Hello""# => r#"<![CDATA[He said "Hello"]]>"#);
1408 serialize_as_cdata!(all_xml_chars: "<>&\"'" => "<![CDATA[<>&\"']]>");
1409
1410 serialize_as_cdata!(with_cdata_end: "foo]]>bar" => "<![CDATA[foo]]]]><![CDATA[>bar]]>");
1411 serialize_as_cdata!(multiple_cdata_ends: "a]]>b]]>c" => "<![CDATA[a]]]]><![CDATA[>b]]]]><![CDATA[>c]]>");
1412 serialize_as_cdata!(starts_with_cdata_end: "]]>hello" => "<![CDATA[]]]]><![CDATA[>hello]]>");
1413 serialize_as_cdata!(ends_with_cdata_end: "hello]]>" => "<![CDATA[hello]]]]><![CDATA[>]]>");
1414 serialize_as_cdata!(only_cdata_end: "]]>" => "<![CDATA[]]]]><![CDATA[>]]>");
1415
1416 serialize_as_cdata!(seq_basic: vec!["foo", "bar", "baz"] => "<![CDATA[foo bar baz]]>");
1417 serialize_as_cdata!(seq_with_space: vec!["hello world", "hello\tworld", "world"] => "<![CDATA[hello world hello\tworld world]]>");
1418 serialize_as_cdata!(seq_with_markup_chars: vec!["<tag>", "&entity", "\"quoted\""] => "<![CDATA[<tag> &entity \"quoted\"]]>");
1419 serialize_as_cdata!(seq_with_cdata_end_split: vec!["foo]]>bar", "test"] => "<![CDATA[foo]]]]><![CDATA[>bar test]]>");
1420 serialize_as_cdata!(tuple_cdata: ("first", 42, "third") => "<![CDATA[first 42 third]]>");
1421 }
1422}