Skip to main content

quick_xml/se/
key.rs

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