Skip to content

Commit 006072e

Browse files
committed
Ensure, that map keys are non-complex types that would not break XML markup
1 parent 0336dcb commit 006072e

File tree

6 files changed

+418
-8
lines changed

6 files changed

+418
-8
lines changed

Changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
### Misc Changes
1818

1919
- [#468]: Content of `DeError::Unsupported` changed from `&'static str` to `Cow<'static, str>`
20+
- [#468]: Ensure, that map keys could only types that serialized as primitives only
2021

2122
[#468]: https://github.com/tafia/quick-xml/pull/468
2223

src/errors.rs

+7
Original file line numberDiff line numberDiff line change
@@ -294,4 +294,11 @@ pub mod serialize {
294294
Self::InvalidFloat(e)
295295
}
296296
}
297+
298+
impl From<fmt::Error> for DeError {
299+
#[inline]
300+
fn from(e: fmt::Error) -> Self {
301+
Self::Custom(e.to_string())
302+
}
303+
}
297304
}

src/se/key.rs

+297
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
use crate::errors::serialize::DeError;
2+
use serde::ser::{Impossible, Serialize, Serializer};
3+
use serde::serde_if_integer128;
4+
use std::fmt::Write;
5+
6+
/// A serializer, that ensures, that only plain types can be serialized,
7+
/// so result can be used as an XML tag or attribute name.
8+
///
9+
/// This serializer does not check that name does not contain characters that
10+
/// [not allowed] in XML names, because in some cases it should pass names
11+
/// that would be filtered on higher level.
12+
///
13+
/// [not allowed]: https://www.w3.org/TR/REC-xml/#sec-common-syn
14+
pub struct XmlNameSerializer<W: Write> {
15+
/// Writer to which this serializer writes content
16+
pub writer: W,
17+
}
18+
19+
impl<W: Write> XmlNameSerializer<W> {
20+
#[inline]
21+
fn write_str(&mut self, value: &str) -> Result<(), DeError> {
22+
Ok(self.writer.write_str(value)?)
23+
}
24+
}
25+
26+
impl<W: Write> Serializer for XmlNameSerializer<W> {
27+
type Ok = W;
28+
type Error = DeError;
29+
30+
type SerializeSeq = Impossible<Self::Ok, Self::Error>;
31+
type SerializeTuple = Impossible<Self::Ok, Self::Error>;
32+
type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
33+
type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
34+
type SerializeMap = Impossible<Self::Ok, Self::Error>;
35+
type SerializeStruct = Impossible<Self::Ok, Self::Error>;
36+
type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
37+
38+
write_primitive!();
39+
40+
fn serialize_str(mut self, value: &str) -> Result<Self::Ok, Self::Error> {
41+
self.write_str(value)?;
42+
Ok(self.writer)
43+
}
44+
45+
/// We cannot store anything, so the absence of a unit and presence of it
46+
/// does not differ, so serialization of unit returns `Err(Unsupported)`
47+
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
48+
Err(DeError::Unsupported(
49+
"unit type `()` cannot be serialized as an XML tag name".into(),
50+
))
51+
}
52+
53+
/// We cannot store both a variant discriminant and a variant value,
54+
/// so serialization of enum newtype variant returns `Err(Unsupported)`
55+
fn serialize_newtype_variant<T: ?Sized + Serialize>(
56+
self,
57+
name: &'static str,
58+
_variant_index: u32,
59+
variant: &'static str,
60+
_value: &T,
61+
) -> Result<Self::Ok, DeError> {
62+
Err(DeError::Unsupported(
63+
format!(
64+
"enum newtype variant `{}::{}` cannot be serialized as an XML tag name",
65+
name, variant
66+
)
67+
.into(),
68+
))
69+
}
70+
71+
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
72+
Err(DeError::Unsupported(
73+
"sequence cannot be serialized as an XML tag name".into(),
74+
))
75+
}
76+
77+
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
78+
Err(DeError::Unsupported(
79+
"tuple cannot be serialized as an XML tag name".into(),
80+
))
81+
}
82+
83+
fn serialize_tuple_struct(
84+
self,
85+
name: &'static str,
86+
_len: usize,
87+
) -> Result<Self::SerializeTupleStruct, Self::Error> {
88+
Err(DeError::Unsupported(
89+
format!(
90+
"tuple struct `{}` cannot be serialized as an XML tag name",
91+
name
92+
)
93+
.into(),
94+
))
95+
}
96+
97+
fn serialize_tuple_variant(
98+
self,
99+
name: &'static str,
100+
_variant_index: u32,
101+
variant: &'static str,
102+
_len: usize,
103+
) -> Result<Self::SerializeTupleVariant, Self::Error> {
104+
Err(DeError::Unsupported(
105+
format!(
106+
"enum tuple variant `{}::{}` cannot be serialized as an XML tag name",
107+
name, variant
108+
)
109+
.into(),
110+
))
111+
}
112+
113+
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
114+
Err(DeError::Unsupported(
115+
"map cannot be serialized as an XML tag name".into(),
116+
))
117+
}
118+
119+
fn serialize_struct(
120+
self,
121+
name: &'static str,
122+
_len: usize,
123+
) -> Result<Self::SerializeStruct, Self::Error> {
124+
Err(DeError::Unsupported(
125+
format!("struct `{}` cannot be serialized as an XML tag name", name).into(),
126+
))
127+
}
128+
129+
fn serialize_struct_variant(
130+
self,
131+
name: &'static str,
132+
_variant_index: u32,
133+
variant: &'static str,
134+
_len: usize,
135+
) -> Result<Self::SerializeStructVariant, Self::Error> {
136+
Err(DeError::Unsupported(
137+
format!(
138+
"enum struct variant `{}::{}` cannot be serialized as an XML tag name",
139+
name, variant
140+
)
141+
.into(),
142+
))
143+
}
144+
}
145+
146+
#[cfg(test)]
147+
mod tests {
148+
use super::*;
149+
use crate::utils::Bytes;
150+
use pretty_assertions::assert_eq;
151+
use serde::Serialize;
152+
use std::collections::BTreeMap;
153+
154+
#[derive(Debug, Serialize, PartialEq)]
155+
struct Unit;
156+
157+
#[derive(Debug, Serialize, PartialEq)]
158+
#[serde(rename = "<\"&'>")]
159+
struct UnitEscaped;
160+
161+
#[derive(Debug, Serialize, PartialEq)]
162+
struct Newtype(bool);
163+
164+
#[derive(Debug, Serialize, PartialEq)]
165+
struct Tuple(&'static str, usize);
166+
167+
#[derive(Debug, Serialize, PartialEq)]
168+
struct Struct {
169+
key: &'static str,
170+
val: usize,
171+
}
172+
173+
#[derive(Debug, Serialize, PartialEq)]
174+
enum Enum {
175+
Unit,
176+
#[serde(rename = "<\"&'>")]
177+
UnitEscaped,
178+
Newtype(bool),
179+
Tuple(&'static str, usize),
180+
Struct {
181+
key: &'static str,
182+
val: usize,
183+
},
184+
}
185+
186+
/// Checks that given `$data` successfully serialized as `$expected`
187+
macro_rules! serialize_as {
188+
($name:ident: $data:expr => $expected:literal) => {
189+
#[test]
190+
fn $name() {
191+
let ser = XmlNameSerializer {
192+
writer: String::new(),
193+
};
194+
195+
let buffer = $data.serialize(ser).unwrap();
196+
assert_eq!(buffer, $expected);
197+
}
198+
};
199+
}
200+
201+
/// Checks that attempt to serialize given `$data` results to a
202+
/// serialization error `$kind` with `$reason`
203+
macro_rules! err {
204+
($name:ident: $data:expr => $kind:ident($reason:literal)) => {
205+
#[test]
206+
fn $name() {
207+
let mut buffer = String::new();
208+
let ser = XmlNameSerializer {
209+
writer: &mut buffer,
210+
};
211+
212+
match $data.serialize(ser).unwrap_err() {
213+
DeError::$kind(e) => assert_eq!(e, $reason),
214+
e => panic!(
215+
"Expected `{}({})`, found `{:?}`",
216+
stringify!($kind),
217+
$reason,
218+
e
219+
),
220+
}
221+
assert_eq!(buffer, "");
222+
}
223+
};
224+
}
225+
226+
serialize_as!(false_: false => "false");
227+
serialize_as!(true_: true => "true");
228+
229+
serialize_as!(i8_: -42i8 => "-42");
230+
serialize_as!(i16_: -4200i16 => "-4200");
231+
serialize_as!(i32_: -42000000i32 => "-42000000");
232+
serialize_as!(i64_: -42000000000000i64 => "-42000000000000");
233+
serialize_as!(isize_: -42000000000000isize => "-42000000000000");
234+
235+
serialize_as!(u8_: 42u8 => "42");
236+
serialize_as!(u16_: 4200u16 => "4200");
237+
serialize_as!(u32_: 42000000u32 => "42000000");
238+
serialize_as!(u64_: 42000000000000u64 => "42000000000000");
239+
serialize_as!(usize_: 42000000000000usize => "42000000000000");
240+
241+
serde_if_integer128! {
242+
serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000");
243+
serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000");
244+
}
245+
246+
serialize_as!(f32_: 4.2f32 => "4.2");
247+
serialize_as!(f64_: 4.2f64 => "4.2");
248+
249+
serialize_as!(char_non_escaped: 'h' => "h");
250+
serialize_as!(char_lt: '<' => "<");
251+
serialize_as!(char_gt: '>' => ">");
252+
serialize_as!(char_amp: '&' => "&");
253+
serialize_as!(char_apos: '\'' => "'");
254+
serialize_as!(char_quot: '"' => "\"");
255+
256+
serialize_as!(str_valid_name: "valid-name" => "valid-name");
257+
serialize_as!(str_space: "string with spaces" => "string with spaces");
258+
serialize_as!(str_lt: "string<" => "string<");
259+
serialize_as!(str_gt: "string>" => "string>");
260+
serialize_as!(str_amp: "string&" => "string&");
261+
serialize_as!(str_apos: "string'" => "string'");
262+
serialize_as!(str_quot: "string\"" => "string\"");
263+
264+
err!(bytes: Bytes(b"<\"escaped & bytes'>")
265+
=> Unsupported("`serialize_bytes` not supported yet"));
266+
267+
serialize_as!(option_none: Option::<&str>::None => "");
268+
serialize_as!(option_some: Some("non-escaped-string") => "non-escaped-string");
269+
270+
err!(unit: ()
271+
=> Unsupported("unit type `()` cannot be serialized as an XML tag name"));
272+
serialize_as!(unit_struct: Unit => "Unit");
273+
serialize_as!(unit_struct_escaped: UnitEscaped => "<\"&'>");
274+
275+
serialize_as!(enum_unit: Enum::Unit => "Unit");
276+
serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "<\"&'>");
277+
278+
serialize_as!(newtype: Newtype(true) => "true");
279+
err!(enum_newtype: Enum::Newtype(false)
280+
=> Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an XML tag name"));
281+
282+
err!(seq: vec![1, 2, 3]
283+
=> Unsupported("sequence cannot be serialized as an XML tag name"));
284+
err!(tuple: ("<\"&'>", "with\t\r\n spaces", 3usize)
285+
=> Unsupported("tuple cannot be serialized as an XML tag name"));
286+
err!(tuple_struct: Tuple("first", 42)
287+
=> Unsupported("tuple struct `Tuple` cannot be serialized as an XML tag name"));
288+
err!(enum_tuple: Enum::Tuple("first", 42)
289+
=> Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an XML tag name"));
290+
291+
err!(map: BTreeMap::from([("_1", 2), ("_3", 4)])
292+
=> Unsupported("map cannot be serialized as an XML tag name"));
293+
err!(struct_: Struct { key: "answer", val: 42 }
294+
=> Unsupported("struct `Struct` cannot be serialized as an XML tag name"));
295+
err!(enum_struct: Enum::Struct { key: "answer", val: 42 }
296+
=> Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an XML tag name"));
297+
}

0 commit comments

Comments
 (0)