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