Skip to content

Commit 06018a4

Browse files
committed
Implement indentation for new serializer. Fix tafia#361
1 parent dc039b5 commit 06018a4

File tree

6 files changed

+1377
-41
lines changed

6 files changed

+1377
-41
lines changed

src/de/simple_type.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -829,7 +829,7 @@ impl<'de> VariantAccess<'de> for SimpleTypeUnitOnly {
829829
mod tests {
830830
use super::*;
831831
use crate::se::simple_type::{QuoteTarget, SimpleTypeSerializer};
832-
use crate::se::QuoteLevel;
832+
use crate::se::{Indent, QuoteLevel};
833833
use crate::utils::{ByteBuf, Bytes};
834834
use serde::de::IgnoredAny;
835835
use serde::{Deserialize, Serialize};
@@ -866,6 +866,7 @@ mod tests {
866866
writer: String::new(),
867867
target: QuoteTarget::Text,
868868
level: QuoteLevel::Full,
869+
indent: Indent::None,
869870
})
870871
.unwrap(),
871872
xml

src/se/content.rs

+245-13
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use crate::errors::serialize::DeError;
44
use crate::se::element::{ElementSerializer, Struct};
55
use crate::se::simple_type::{QuoteTarget, SimpleTypeSerializer};
6-
use crate::se::{QuoteLevel, XmlName};
6+
use crate::se::{Indent, QuoteLevel, XmlName};
77
use serde::ser::{
88
Impossible, Serialize, SerializeSeq, SerializeTuple, SerializeTupleStruct, Serializer,
99
};
@@ -39,23 +39,36 @@ macro_rules! write_primitive {
3939
/// and maps serialized. Unlike `SimpleTypeSerializer` it supports any types in
4040
/// sequences and serializes them as list of elements, but that has drawbacks.
4141
/// Sequence of primitives would be serialized without delimiters and it will be
42-
/// impossible to distinguish between them.
43-
pub struct ContentSerializer<W: Write> {
42+
/// impossible to distinguish between them. Even worse, when serializing with
43+
/// indent, sequence of strings become one big string with additional content
44+
/// and it would be impossible to distinguish between content of the original
45+
/// strings and inserted indent characters.
46+
pub struct ContentSerializer<'i, W: Write> {
4447
pub writer: W,
4548
/// Defines which XML characters need to be escaped in text content
4649
pub level: QuoteLevel,
50+
/// Current indentation level
51+
pub(super) indent: Indent<'i>,
52+
/// If `true`, then current indent will be written before write the content,
53+
/// but only if content is not empty
54+
pub write_indent: bool,
4755
//TODO: add settings to disallow consequent serialization of primitives
4856
}
4957

50-
impl<W: Write> ContentSerializer<W> {
58+
impl<'i, W: Write> ContentSerializer<'i, W> {
5159
/// Turns this serializer into serializer of a text content
5260
#[inline]
53-
pub fn into_simple_type_serializer(self) -> SimpleTypeSerializer<W> {
61+
pub fn into_simple_type_serializer(self) -> SimpleTypeSerializer<'i, W> {
5462
//TODO: Customization point: choose between CDATA and Text representation
5563
SimpleTypeSerializer {
5664
writer: self.writer,
5765
target: QuoteTarget::Text,
5866
level: self.level,
67+
indent: if self.write_indent {
68+
self.indent
69+
} else {
70+
Indent::None
71+
},
5972
}
6073
}
6174

@@ -66,12 +79,15 @@ impl<W: Write> ContentSerializer<W> {
6679
ContentSerializer {
6780
writer: &mut self.writer,
6881
level: self.level,
82+
indent: self.indent.borrow(),
83+
write_indent: self.write_indent,
6984
}
7085
}
7186

7287
/// Writes `name` as self-closed tag
7388
#[inline]
7489
pub(super) fn write_empty(mut self, name: XmlName) -> Result<W, DeError> {
90+
self.write_indent()?;
7591
self.writer.write_char('<')?;
7692
self.writer.write_str(name.0)?;
7793
self.writer.write_str("/>")?;
@@ -81,8 +97,9 @@ impl<W: Write> ContentSerializer<W> {
8197
/// Writes simple type content between `name` tags
8298
pub(super) fn write_wrapped<S>(mut self, name: XmlName, serialize: S) -> Result<W, DeError>
8399
where
84-
S: FnOnce(SimpleTypeSerializer<W>) -> Result<W, DeError>,
100+
S: FnOnce(SimpleTypeSerializer<'i, W>) -> Result<W, DeError>,
85101
{
102+
self.write_indent()?;
86103
self.writer.write_char('<')?;
87104
self.writer.write_str(name.0)?;
88105
self.writer.write_char('>')?;
@@ -94,19 +111,27 @@ impl<W: Write> ContentSerializer<W> {
94111
writer.write_char('>')?;
95112
Ok(writer)
96113
}
114+
115+
pub(super) fn write_indent(&mut self) -> Result<(), DeError> {
116+
if self.write_indent {
117+
self.indent.write_indent(&mut self.writer)?;
118+
self.write_indent = false;
119+
}
120+
Ok(())
121+
}
97122
}
98123

99-
impl<W: Write> Serializer for ContentSerializer<W> {
124+
impl<'i, W: Write> Serializer for ContentSerializer<'i, W> {
100125
type Ok = W;
101126
type Error = DeError;
102127

103128
type SerializeSeq = Self;
104129
type SerializeTuple = Self;
105130
type SerializeTupleStruct = Self;
106-
type SerializeTupleVariant = ElementSerializer<'static, W>;
131+
type SerializeTupleVariant = ElementSerializer<'i, W>;
107132
type SerializeMap = Impossible<Self::Ok, Self::Error>;
108133
type SerializeStruct = Impossible<Self::Ok, Self::Error>;
109-
type SerializeStructVariant = Struct<'static, W>;
134+
type SerializeStructVariant = Struct<'i, W>;
110135

111136
write_primitive!(serialize_bool(bool));
112137

@@ -129,9 +154,17 @@ impl<W: Write> Serializer for ContentSerializer<W> {
129154
write_primitive!(serialize_f64(f64));
130155

131156
write_primitive!(serialize_char(char));
132-
write_primitive!(serialize_str(&str));
133157
write_primitive!(serialize_bytes(&[u8]));
134158

159+
#[inline]
160+
fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {
161+
if value.is_empty() {
162+
Ok(self.writer)
163+
} else {
164+
self.into_simple_type_serializer().serialize_str(value)
165+
}
166+
}
167+
135168
/// Does not write anything
136169
#[inline]
137170
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
@@ -261,7 +294,7 @@ impl<W: Write> Serializer for ContentSerializer<W> {
261294
}
262295
}
263296

264-
impl<W: Write> SerializeSeq for ContentSerializer<W> {
297+
impl<'i, W: Write> SerializeSeq for ContentSerializer<'i, W> {
265298
type Ok = W;
266299
type Error = DeError;
267300

@@ -270,6 +303,8 @@ impl<W: Write> SerializeSeq for ContentSerializer<W> {
270303
T: ?Sized + Serialize,
271304
{
272305
value.serialize(self.new_seq_element_serializer())?;
306+
// Write indent for next element
307+
self.write_indent = true;
273308
Ok(())
274309
}
275310

@@ -279,7 +314,7 @@ impl<W: Write> SerializeSeq for ContentSerializer<W> {
279314
}
280315
}
281316

282-
impl<W: Write> SerializeTuple for ContentSerializer<W> {
317+
impl<'i, W: Write> SerializeTuple for ContentSerializer<'i, W> {
283318
type Ok = W;
284319
type Error = DeError;
285320

@@ -297,7 +332,7 @@ impl<W: Write> SerializeTuple for ContentSerializer<W> {
297332
}
298333
}
299334

300-
impl<W: Write> SerializeTupleStruct for ContentSerializer<W> {
335+
impl<'i, W: Write> SerializeTupleStruct for ContentSerializer<'i, W> {
301336
type Ok = W;
302337
type Error = DeError;
303338

@@ -442,6 +477,8 @@ pub(super) mod tests {
442477
let ser = ContentSerializer {
443478
writer: String::new(),
444479
level: QuoteLevel::Full,
480+
indent: Indent::None,
481+
write_indent: false,
445482
};
446483

447484
let buffer = $data.serialize(ser).unwrap();
@@ -460,6 +497,8 @@ pub(super) mod tests {
460497
let ser = ContentSerializer {
461498
writer: &mut buffer,
462499
level: QuoteLevel::Full,
500+
indent: Indent::None,
501+
write_indent: false,
463502
};
464503

465504
match $data.serialize(ser).unwrap_err() {
@@ -611,4 +650,197 @@ pub(super) mod tests {
611650
=> r#"<AttributesAfter val="42"><key>answer</key></AttributesAfter>"#);
612651
}
613652
}
653+
654+
mod with_indent {
655+
use super::Struct;
656+
use super::*;
657+
use crate::writer::Indentation;
658+
use pretty_assertions::assert_eq;
659+
660+
/// Checks that given `$data` successfully serialized as `$expected`
661+
macro_rules! serialize_as {
662+
($name:ident: $data:expr => $expected:literal) => {
663+
#[test]
664+
fn $name() {
665+
let ser = ContentSerializer {
666+
writer: String::new(),
667+
level: QuoteLevel::Full,
668+
indent: Indent::Owned(Indentation::new(b' ', 2)),
669+
write_indent: false,
670+
};
671+
672+
let buffer = $data.serialize(ser).unwrap();
673+
assert_eq!(buffer, $expected);
674+
}
675+
};
676+
}
677+
678+
/// Checks that attempt to serialize given `$data` results to a
679+
/// serialization error `$kind` with `$reason`
680+
macro_rules! err {
681+
($name:ident: $data:expr => $kind:ident($reason:literal)) => {
682+
#[test]
683+
fn $name() {
684+
let mut buffer = String::new();
685+
let ser = ContentSerializer {
686+
writer: &mut buffer,
687+
level: QuoteLevel::Full,
688+
indent: Indent::Owned(Indentation::new(b' ', 2)),
689+
write_indent: false,
690+
};
691+
692+
match $data.serialize(ser).unwrap_err() {
693+
DeError::$kind(e) => assert_eq!(e, $reason),
694+
e => panic!(
695+
"Expected `{}({})`, found `{:?}`",
696+
stringify!($kind),
697+
$reason,
698+
e
699+
),
700+
}
701+
// We can write something before fail
702+
// assert_eq!(buffer, "");
703+
}
704+
};
705+
}
706+
707+
serialize_as!(false_: false => "false");
708+
serialize_as!(true_: true => "true");
709+
710+
serialize_as!(i8_: -42i8 => "-42");
711+
serialize_as!(i16_: -4200i16 => "-4200");
712+
serialize_as!(i32_: -42000000i32 => "-42000000");
713+
serialize_as!(i64_: -42000000000000i64 => "-42000000000000");
714+
serialize_as!(isize_: -42000000000000isize => "-42000000000000");
715+
716+
serialize_as!(u8_: 42u8 => "42");
717+
serialize_as!(u16_: 4200u16 => "4200");
718+
serialize_as!(u32_: 42000000u32 => "42000000");
719+
serialize_as!(u64_: 42000000000000u64 => "42000000000000");
720+
serialize_as!(usize_: 42000000000000usize => "42000000000000");
721+
722+
serde_if_integer128! {
723+
serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
724+
serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000");
725+
}
726+
727+
serialize_as!(f32_: 4.2f32 => "4.2");
728+
serialize_as!(f64_: 4.2f64 => "4.2");
729+
730+
serialize_as!(char_non_escaped: 'h' => "h");
731+
serialize_as!(char_lt: '<' => "&lt;");
732+
serialize_as!(char_gt: '>' => "&gt;");
733+
serialize_as!(char_amp: '&' => "&amp;");
734+
serialize_as!(char_apos: '\'' => "&apos;");
735+
serialize_as!(char_quot: '"' => "&quot;");
736+
//TODO: add a setting to escape leading/trailing spaces, in order to
737+
// pretty-print does not change the content
738+
serialize_as!(char_space: ' ' => " ");
739+
740+
serialize_as!(str_non_escaped: "non-escaped string" => "non-escaped string");
741+
serialize_as!(str_escaped: "<\"escaped & string'>" => "&lt;&quot;escaped &amp; string&apos;&gt;");
742+
743+
err!(bytes: Bytes(b"<\"escaped & bytes'>") => Unsupported("`serialize_bytes` not supported yet"));
744+
745+
serialize_as!(option_none: Option::<Enum>::None => "");
746+
serialize_as!(option_some: Some(Enum::Unit) => "<Unit/>");
747+
748+
serialize_as!(unit: () => "");
749+
serialize_as!(unit_struct: Unit => "");
750+
serialize_as!(unit_struct_escaped: UnitEscaped => "");
751+
752+
// Unlike SimpleTypeSerializer, enumeration values serialized as tags
753+
serialize_as!(enum_unit: Enum::Unit => "<Unit/>");
754+
err!(enum_unit_escaped: Enum::UnitEscaped
755+
=> Unsupported("character `<` is not allowed at the start of an XML name `<\"&'>`"));
756+
757+
// Newtypes recursively applies ContentSerializer
758+
serialize_as!(newtype: Newtype(42) => "42");
759+
serialize_as!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>");
760+
761+
// Note that sequences of primitives serialized without delimiters other that indent!
762+
serialize_as!(seq: vec![1, 2, 3]
763+
=> "1\n\
764+
2\n\
765+
3");
766+
serialize_as!(seq_empty: Vec::<usize>::new() => "");
767+
serialize_as!(tuple: ("<\"&'>", "with\t\r\n spaces", 3usize)
768+
=> "&lt;&quot;&amp;&apos;&gt;\n\
769+
with\t\r\n spaces\n\
770+
3");
771+
serialize_as!(tuple_struct: Tuple("first", 42)
772+
=> "first\n\
773+
42");
774+
serialize_as!(enum_tuple: Enum::Tuple("first", 42)
775+
=> "<Tuple>first</Tuple>\n\
776+
<Tuple>42</Tuple>");
777+
778+
// Structured types cannot be serialized without surrounding tag, which
779+
// only `enum` can provide
780+
err!(map: BTreeMap::from([("_1", 2), ("_3", 4)])
781+
=> Unsupported("serialization of map is not supported in `#any` field"));
782+
err!(struct_: Struct { key: "answer", val: (42, 42) }
783+
=> Unsupported("serialization of struct `Struct` is not supported in `#any` field"));
784+
serialize_as!(enum_struct: Enum::Struct { key: "answer", val: (42, 42) }
785+
=> "<Struct>\n \
786+
<key>answer</key>\n \
787+
<val>42</val>\n \
788+
<val>42</val>\n\
789+
</Struct>");
790+
791+
/// Special field name `#text` should be serialized as text content
792+
mod text {
793+
use super::*;
794+
use pretty_assertions::assert_eq;
795+
796+
err!(map: BTreeMap::from([("#text", 2), ("_3", 4)])
797+
=> Unsupported("serialization of map is not supported in `#any` field"));
798+
err!(struct_:
799+
Text {
800+
before: "answer",
801+
content: (42, 42),
802+
after: "answer",
803+
}
804+
=> Unsupported("serialization of struct `Text` is not supported in `#any` field"));
805+
serialize_as!(enum_struct:
806+
SpecialEnum::Text {
807+
before: "answer",
808+
content: (42, 42),
809+
after: "answer",
810+
}
811+
=> "<Text>\n \
812+
<before>answer</before>\n \
813+
42 42\n \
814+
<after>answer</after>\n\
815+
</Text>");
816+
}
817+
818+
mod attributes {
819+
use super::*;
820+
use pretty_assertions::assert_eq;
821+
822+
err!(map_attr: BTreeMap::from([("@key1", 1), ("@key2", 2)])
823+
=> Unsupported("serialization of map is not supported in `#any` field"));
824+
err!(map_mixed: BTreeMap::from([("@key1", 1), ("key2", 2)])
825+
=> Unsupported("serialization of map is not supported in `#any` field"));
826+
827+
err!(struct_: Attributes { key: "answer", val: (42, 42) }
828+
=> Unsupported("serialization of struct `Attributes` is not supported in `#any` field"));
829+
err!(struct_before: AttributesBefore { key: "answer", val: 42 }
830+
=> Unsupported("serialization of struct `AttributesBefore` is not supported in `#any` field"));
831+
err!(struct_after: AttributesAfter { key: "answer", val: 42 }
832+
=> Unsupported("serialization of struct `AttributesAfter` is not supported in `#any` field"));
833+
834+
serialize_as!(enum_: Enum::Attributes { key: "answer", val: (42, 42) }
835+
=> r#"<Attributes key="answer" val="42 42"/>"#);
836+
serialize_as!(enum_before: Enum::AttributesBefore { key: "answer", val: 42 }
837+
=> "<AttributesBefore key=\"answer\">\n \
838+
<val>42</val>\n\
839+
</AttributesBefore>");
840+
serialize_as!(enum_after: Enum::AttributesAfter { key: "answer", val: 42 }
841+
=> "<AttributesAfter val=\"42\">\n \
842+
<key>answer</key>\n\
843+
</AttributesAfter>");
844+
}
845+
}
614846
}

0 commit comments

Comments
 (0)