1 | //! Contains Serde `Serializer` for XML [simple types] [as defined] in the XML Schema.
|
2 | //!
|
3 | //! [simple types]: https://www.w3schools.com/xml/el_simpletype.asp
|
4 | //! [as defined]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
|
5 |
|
6 | use crate::errors::serialize::DeError;
|
7 | use crate::escapei::_escape;
|
8 | use crate::se::{Indent, QuoteLevel};
|
9 | use serde::ser::{
|
10 | Impossible, Serialize, SerializeSeq, SerializeTuple, SerializeTupleStruct, Serializer,
|
11 | };
|
12 | use serde::serde_if_integer128;
|
13 | use std::borrow::Cow;
|
14 | use std::fmt::Write;
|
15 |
|
16 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
17 | pub enum QuoteTarget {
|
18 | /// Escape data for a text content. No additional escape symbols
|
19 | Text,
|
20 | /// Escape data for a double-quoted attribute. `"` always escaped
|
21 | DoubleQAttr,
|
22 | /// Escape data for a single-quoted attribute. `'` always escaped
|
23 | SingleQAttr,
|
24 | }
|
25 |
|
26 | /// Escapes atomic value that could be part of a `xs:list`. All whitespace characters
|
27 | /// additionally escaped
|
28 | fn escape_item(value: &str, target: QuoteTarget, level: QuoteLevel) -> Cow<str> {
|
29 | use QuoteLevel::*;
|
30 | use QuoteTarget::*;
|
31 |
|
32 | match (target, level) {
|
33 | (_, Full) => _escape(value, |ch| match ch {
|
34 | // Spaces used as delimiters of list items, cannot be used in the item
|
35 | b' ' | b' \r' | b' \n' | b' \t' => true,
|
36 | // Required characters to escape
|
37 | b'&' | b'<' | b'>' | b' \'' | b' \"' => true,
|
38 | _ => false,
|
39 | }),
|
40 | //----------------------------------------------------------------------
|
41 | (Text, Partial) => _escape(value, |ch| match ch {
|
42 | // Spaces used as delimiters of list items, cannot be used in the item
|
43 | b' ' | b' \r' | b' \n' | b' \t' => true,
|
44 | // Required characters to escape
|
45 | b'&' | b'<' | b'>' => true,
|
46 | _ => false,
|
47 | }),
|
48 | (Text, Minimal) => _escape(value, |ch| match ch {
|
49 | // Spaces used as delimiters of list items, cannot be used in the item
|
50 | b' ' | b' \r' | b' \n' | b' \t' => true,
|
51 | // Required characters to escape
|
52 | b'&' | b'<' => true,
|
53 | _ => false,
|
54 | }),
|
55 | //----------------------------------------------------------------------
|
56 | (DoubleQAttr, Partial) => _escape(value, |ch| match ch {
|
57 | // Spaces used as delimiters of list items, cannot be used in the item
|
58 | b' ' | b' \r' | b' \n' | b' \t' => true,
|
59 | // Required characters to escape
|
60 | b'&' | b'<' | b'>' => true,
|
61 | // Double quoted attribute should escape quote
|
62 | b'"' => true,
|
63 | _ => false,
|
64 | }),
|
65 | (DoubleQAttr, Minimal) => _escape(value, |ch| match ch {
|
66 | // Spaces used as delimiters of list items, cannot be used in the item
|
67 | b' ' | b' \r' | b' \n' | b' \t' => true,
|
68 | // Required characters to escape
|
69 | b'&' | b'<' => true,
|
70 | // Double quoted attribute should escape quote
|
71 | b'"' => true,
|
72 | _ => false,
|
73 | }),
|
74 | //----------------------------------------------------------------------
|
75 | (SingleQAttr, Partial) => _escape(value, |ch| match ch {
|
76 | // Spaces used as delimiters of list items
|
77 | b' ' | b' \r' | b' \n' | b' \t' => true,
|
78 | // Required characters to escape
|
79 | b'&' | b'<' | b'>' => true,
|
80 | // Single quoted attribute should escape quote
|
81 | b' \'' => true,
|
82 | _ => false,
|
83 | }),
|
84 | (SingleQAttr, Minimal) => _escape(value, |ch| match ch {
|
85 | // Spaces used as delimiters of list items
|
86 | b' ' | b' \r' | b' \n' | b' \t' => true,
|
87 | // Required characters to escape
|
88 | b'&' | b'<' => true,
|
89 | // Single quoted attribute should escape quote
|
90 | b' \'' => true,
|
91 | _ => false,
|
92 | }),
|
93 | }
|
94 | }
|
95 |
|
96 | /// Escapes XSD simple type value
|
97 | fn escape_list(value: &str, target: QuoteTarget, level: QuoteLevel) -> Cow<str> {
|
98 | use QuoteLevel::*;
|
99 | use QuoteTarget::*;
|
100 |
|
101 | match (target, level) {
|
102 | (_, Full) => _escape(value, |ch| match ch {
|
103 | // Required characters to escape
|
104 | b'&' | b'<' | b'>' | b' \'' | b' \"' => true,
|
105 | _ => false,
|
106 | }),
|
107 | //----------------------------------------------------------------------
|
108 | (Text, Partial) => _escape(value, |ch| match ch {
|
109 | // Required characters to escape
|
110 | b'&' | b'<' | b'>' => true,
|
111 | _ => false,
|
112 | }),
|
113 | (Text, Minimal) => _escape(value, |ch| match ch {
|
114 | // Required characters to escape
|
115 | b'&' | b'<' => true,
|
116 | _ => false,
|
117 | }),
|
118 | //----------------------------------------------------------------------
|
119 | (DoubleQAttr, Partial) => _escape(value, |ch| match ch {
|
120 | // Required characters to escape
|
121 | b'&' | b'<' | b'>' => true,
|
122 | // Double quoted attribute should escape quote
|
123 | b'"' => true,
|
124 | _ => false,
|
125 | }),
|
126 | (DoubleQAttr, Minimal) => _escape(value, |ch| match ch {
|
127 | // Required characters to escape
|
128 | b'&' | b'<' => true,
|
129 | // Double quoted attribute should escape quote
|
130 | b'"' => true,
|
131 | _ => false,
|
132 | }),
|
133 | //----------------------------------------------------------------------
|
134 | (SingleQAttr, Partial) => _escape(value, |ch| match ch {
|
135 | // Required characters to escape
|
136 | b'&' | b'<' | b'>' => true,
|
137 | // Single quoted attribute should escape quote
|
138 | b' \'' => true,
|
139 | _ => false,
|
140 | }),
|
141 | (SingleQAttr, Minimal) => _escape(value, |ch| match ch {
|
142 | // Required characters to escape
|
143 | b'&' | b'<' => true,
|
144 | // Single quoted attribute should escape quote
|
145 | b' \'' => true,
|
146 | _ => false,
|
147 | }),
|
148 | }
|
149 | }
|
150 |
|
151 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
152 |
|
153 | /// A serializer that handles ordinary [simple type definition][item] with
|
154 | /// `{variety} = atomic`, or an ordinary [simple type] definition with
|
155 | /// `{variety} = union` whose basic members are all atomic.
|
156 | ///
|
157 | /// This serializer can serialize only primitive types:
|
158 | /// - numbers
|
159 | /// - booleans
|
160 | /// - strings
|
161 | /// - units
|
162 | /// - options
|
163 | /// - unit variants of enums
|
164 | ///
|
165 | /// Identifiers represented as strings and serialized accordingly.
|
166 | ///
|
167 | /// Serialization of all other types returns [`Unsupported`][DeError::Unsupported] error.
|
168 | ///
|
169 | /// [item]: https://www.w3.org/TR/xmlschema11-1/#std-item_type_definition
|
170 | /// [simple type]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
|
171 | pub struct AtomicSerializer<W: Write> {
|
172 | pub writer: W,
|
173 | pub target: QuoteTarget,
|
174 | /// Defines which XML characters need to be escaped
|
175 | pub level: QuoteLevel,
|
176 | }
|
177 |
|
178 | impl<W: Write> AtomicSerializer<W> {
|
179 | fn write_str(&mut self, value: &str) -> Result<(), DeError> {
|
180 | Ok(self
|
181 | .writer
|
182 | .write_str(&escape_item(value, self.target, self.level))?)
|
183 | }
|
184 | }
|
185 |
|
186 | impl<W: Write> Serializer for AtomicSerializer<W> {
|
187 | type Ok = W;
|
188 | type Error = DeError;
|
189 |
|
190 | type SerializeSeq = Impossible<Self::Ok, Self::Error>;
|
191 | type SerializeTuple = Impossible<Self::Ok, Self::Error>;
|
192 | type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
|
193 | type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
|
194 | type SerializeMap = Impossible<Self::Ok, Self::Error>;
|
195 | type SerializeStruct = Impossible<Self::Ok, Self::Error>;
|
196 | type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
|
197 |
|
198 | write_primitive!();
|
199 |
|
200 | fn serialize_str(mut self, value: &str) -> Result<Self::Ok, Self::Error> {
|
201 | self.write_str(value)?;
|
202 | Ok(self.writer)
|
203 | }
|
204 |
|
205 | /// We cannot store anything, so the absence of a unit and presence of it
|
206 | /// does not differ, so serialization of unit returns `Err(Unsupported)`
|
207 | fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
208 | Err(DeError::Unsupported(
|
209 | "unit type `()` cannot be serialized as an `xs:list` item" .into(),
|
210 | ))
|
211 | }
|
212 |
|
213 | /// We cannot store anything, so the absence of a unit and presence of it
|
214 | /// does not differ, so serialization of unit returns `Err(Unsupported)`
|
215 | fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
|
216 | Err(DeError::Unsupported(
|
217 | format!(
|
218 | "unit struct ` {}` cannot be serialized as an `xs:list` item" ,
|
219 | name
|
220 | )
|
221 | .into(),
|
222 | ))
|
223 | }
|
224 |
|
225 | /// We cannot store both a variant discriminant and a variant value,
|
226 | /// so serialization of enum newtype variant returns `Err(Unsupported)`
|
227 | fn serialize_newtype_variant<T: ?Sized + Serialize>(
|
228 | self,
|
229 | name: &'static str,
|
230 | _variant_index: u32,
|
231 | variant: &'static str,
|
232 | _value: &T,
|
233 | ) -> Result<Self::Ok, DeError> {
|
234 | Err(DeError::Unsupported(
|
235 | format!(
|
236 | "enum newtype variant ` {}:: {}` cannot be serialized as an `xs:list` item" ,
|
237 | name, variant
|
238 | )
|
239 | .into(),
|
240 | ))
|
241 | }
|
242 |
|
243 | fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
244 | Err(DeError::Unsupported(
|
245 | "sequence cannot be serialized as an `xs:list` item" .into(),
|
246 | ))
|
247 | }
|
248 |
|
249 | fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
250 | Err(DeError::Unsupported(
|
251 | "tuple cannot be serialized as an `xs:list` item" .into(),
|
252 | ))
|
253 | }
|
254 |
|
255 | fn serialize_tuple_struct(
|
256 | self,
|
257 | name: &'static str,
|
258 | _len: usize,
|
259 | ) -> Result<Self::SerializeTupleStruct, Self::Error> {
|
260 | Err(DeError::Unsupported(
|
261 | format!(
|
262 | "tuple struct ` {}` cannot be serialized as an `xs:list` item" ,
|
263 | name
|
264 | )
|
265 | .into(),
|
266 | ))
|
267 | }
|
268 |
|
269 | fn serialize_tuple_variant(
|
270 | self,
|
271 | name: &'static str,
|
272 | _variant_index: u32,
|
273 | variant: &'static str,
|
274 | _len: usize,
|
275 | ) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
276 | Err(DeError::Unsupported(
|
277 | format!(
|
278 | "enum tuple variant ` {}:: {}` cannot be serialized as an `xs:list` item" ,
|
279 | name, variant
|
280 | )
|
281 | .into(),
|
282 | ))
|
283 | }
|
284 |
|
285 | fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
286 | Err(DeError::Unsupported(
|
287 | "map cannot be serialized as an `xs:list` item" .into(),
|
288 | ))
|
289 | }
|
290 |
|
291 | fn serialize_struct(
|
292 | self,
|
293 | name: &'static str,
|
294 | _len: usize,
|
295 | ) -> Result<Self::SerializeStruct, Self::Error> {
|
296 | Err(DeError::Unsupported(
|
297 | format!(
|
298 | "struct ` {}` cannot be serialized as an `xs:list` item" ,
|
299 | name
|
300 | )
|
301 | .into(),
|
302 | ))
|
303 | }
|
304 |
|
305 | fn serialize_struct_variant(
|
306 | self,
|
307 | name: &'static str,
|
308 | _variant_index: u32,
|
309 | variant: &'static str,
|
310 | _len: usize,
|
311 | ) -> Result<Self::SerializeStructVariant, Self::Error> {
|
312 | Err(DeError::Unsupported(
|
313 | format!(
|
314 | "enum struct variant ` {}:: {}` cannot be serialized as an `xs:list` item" ,
|
315 | name, variant
|
316 | )
|
317 | .into(),
|
318 | ))
|
319 | }
|
320 | }
|
321 |
|
322 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
323 |
|
324 | /// A serializer for a values representing XSD [simple types], which used in:
|
325 | /// - attribute values (`<... ...="value" ...>`)
|
326 | /// - text content (`<...>text</...>`)
|
327 | /// - CDATA content (`<...><![CDATA[cdata]]></...>`)
|
328 | ///
|
329 | /// [simple types]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
|
330 | pub struct SimpleTypeSerializer<'i, W: Write> {
|
331 | /// Writer to which this serializer writes content
|
332 | pub writer: W,
|
333 | /// Target for which element is serializing. Affects additional characters to escape.
|
334 | pub target: QuoteTarget,
|
335 | /// Defines which XML characters need to be escaped
|
336 | pub level: QuoteLevel,
|
337 | /// Indent that should be written before the content if content is not an empty string
|
338 | pub(crate) indent: Indent<'i>,
|
339 | }
|
340 |
|
341 | impl<'i, W: Write> SimpleTypeSerializer<'i, W> {
|
342 | fn write_str(&mut self, value: &str) -> Result<(), DeError> {
|
343 | self.indent.write_indent(&mut self.writer)?;
|
344 | Ok(self
|
345 | .writer
|
346 | .write_str(&escape_list(value, self.target, self.level))?)
|
347 | }
|
348 | }
|
349 |
|
350 | impl<'i, W: Write> Serializer for SimpleTypeSerializer<'i, W> {
|
351 | type Ok = W;
|
352 | type Error = DeError;
|
353 |
|
354 | type SerializeSeq = SimpleSeq<'i, W>;
|
355 | type SerializeTuple = SimpleSeq<'i, W>;
|
356 | type SerializeTupleStruct = SimpleSeq<'i, W>;
|
357 | type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
|
358 | type SerializeMap = Impossible<Self::Ok, Self::Error>;
|
359 | type SerializeStruct = Impossible<Self::Ok, Self::Error>;
|
360 | type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
|
361 |
|
362 | write_primitive!();
|
363 |
|
364 | fn serialize_str(mut self, value: &str) -> Result<Self::Ok, Self::Error> {
|
365 | if value.is_empty() {
|
366 | self.indent = Indent::None;
|
367 | }
|
368 | self.write_str(value)?;
|
369 | Ok(self.writer)
|
370 | }
|
371 |
|
372 | /// Does not write anything
|
373 | fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
374 | Ok(self.writer)
|
375 | }
|
376 |
|
377 | /// Does not write anything
|
378 | fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
|
379 | Ok(self.writer)
|
380 | }
|
381 |
|
382 | /// We cannot store both a variant discriminant and a variant value,
|
383 | /// so serialization of enum newtype variant returns `Err(Unsupported)`
|
384 | fn serialize_newtype_variant<T: ?Sized + Serialize>(
|
385 | self,
|
386 | name: &'static str,
|
387 | _variant_index: u32,
|
388 | variant: &'static str,
|
389 | _value: &T,
|
390 | ) -> Result<Self::Ok, DeError> {
|
391 | Err(DeError::Unsupported(
|
392 | format!("enum newtype variant ` {}:: {}` cannot be serialized as an attribute or text content value" , name, variant).into(),
|
393 | ))
|
394 | }
|
395 |
|
396 | #[inline ]
|
397 | fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
398 | Ok(SimpleSeq {
|
399 | writer: self.writer,
|
400 | target: self.target,
|
401 | level: self.level,
|
402 | first: true,
|
403 | indent: self.indent,
|
404 | })
|
405 | }
|
406 |
|
407 | #[inline ]
|
408 | fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
409 | self.serialize_seq(None)
|
410 | }
|
411 |
|
412 | #[inline ]
|
413 | fn serialize_tuple_struct(
|
414 | self,
|
415 | _name: &'static str,
|
416 | _len: usize,
|
417 | ) -> Result<Self::SerializeTupleStruct, Self::Error> {
|
418 | self.serialize_seq(None)
|
419 | }
|
420 |
|
421 | fn serialize_tuple_variant(
|
422 | self,
|
423 | name: &'static str,
|
424 | _variant_index: u32,
|
425 | variant: &'static str,
|
426 | _len: usize,
|
427 | ) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
428 | Err(DeError::Unsupported(
|
429 | format!("enum tuple variant ` {}:: {}` cannot be serialized as an attribute or text content value" , name, variant).into(),
|
430 | ))
|
431 | }
|
432 |
|
433 | fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
434 | Err(DeError::Unsupported(
|
435 | "map cannot be serialized as an attribute or text content value" .into(),
|
436 | ))
|
437 | }
|
438 |
|
439 | fn serialize_struct(
|
440 | self,
|
441 | name: &'static str,
|
442 | _len: usize,
|
443 | ) -> Result<Self::SerializeStruct, Self::Error> {
|
444 | Err(DeError::Unsupported(
|
445 | format!(
|
446 | "struct ` {}` cannot be serialized as an attribute or text content value" ,
|
447 | name
|
448 | )
|
449 | .into(),
|
450 | ))
|
451 | }
|
452 |
|
453 | fn serialize_struct_variant(
|
454 | self,
|
455 | name: &'static str,
|
456 | _variant_index: u32,
|
457 | variant: &'static str,
|
458 | _len: usize,
|
459 | ) -> Result<Self::SerializeStructVariant, Self::Error> {
|
460 | Err(DeError::Unsupported(
|
461 | format!("enum struct variant ` {}:: {}` cannot be serialized as an attribute or text content value" , name, variant).into(),
|
462 | ))
|
463 | }
|
464 | }
|
465 |
|
466 | /// Serializer for a sequence of atomic values delimited by space
|
467 | pub struct SimpleSeq<'i, W: Write> {
|
468 | writer: W,
|
469 | target: QuoteTarget,
|
470 | level: QuoteLevel,
|
471 | /// If `true`, nothing was written yet
|
472 | first: bool,
|
473 | /// Indent that should be written before the content if content is not an empty string
|
474 | indent: Indent<'i>,
|
475 | }
|
476 |
|
477 | impl<'i, W: Write> SerializeSeq for SimpleSeq<'i, W> {
|
478 | type Ok = W;
|
479 | type Error = DeError;
|
480 |
|
481 | fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
482 | where
|
483 | T: ?Sized + Serialize,
|
484 | {
|
485 | // Write indent for the first element and delimiter for others
|
486 | //FIXME: sequence with only empty strings will be serialized as indent only + delimiters
|
487 | if self.first {
|
488 | self.indent.write_indent(&mut self.writer)?;
|
489 | } else {
|
490 | self.writer.write_char(' ' )?;
|
491 | }
|
492 | self.first = false;
|
493 | value.serialize(AtomicSerializer {
|
494 | writer: &mut self.writer,
|
495 | target: self.target,
|
496 | level: self.level,
|
497 | })?;
|
498 | Ok(())
|
499 | }
|
500 |
|
501 | #[inline ]
|
502 | fn end(self) -> Result<Self::Ok, Self::Error> {
|
503 | Ok(self.writer)
|
504 | }
|
505 | }
|
506 |
|
507 | impl<'i, W: Write> SerializeTuple for SimpleSeq<'i, W> {
|
508 | type Ok = W;
|
509 | type Error = DeError;
|
510 |
|
511 | #[inline ]
|
512 | fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
513 | where
|
514 | T: ?Sized + Serialize,
|
515 | {
|
516 | <Self as SerializeSeq>::serialize_element(self, value)
|
517 | }
|
518 |
|
519 | #[inline ]
|
520 | fn end(self) -> Result<Self::Ok, Self::Error> {
|
521 | <Self as SerializeSeq>::end(self)
|
522 | }
|
523 | }
|
524 |
|
525 | impl<'i, W: Write> SerializeTupleStruct for SimpleSeq<'i, W> {
|
526 | type Ok = W;
|
527 | type Error = DeError;
|
528 |
|
529 | #[inline ]
|
530 | fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
531 | where
|
532 | T: ?Sized + Serialize,
|
533 | {
|
534 | <Self as SerializeSeq>::serialize_element(self, value)
|
535 | }
|
536 |
|
537 | #[inline ]
|
538 | fn end(self) -> Result<Self::Ok, Self::Error> {
|
539 | <Self as SerializeSeq>::end(self)
|
540 | }
|
541 | }
|
542 |
|
543 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
544 |
|
545 | #[cfg (test)]
|
546 | mod tests {
|
547 | use super::*;
|
548 | use crate::utils::Bytes;
|
549 | use serde::Serialize;
|
550 | use std::collections::BTreeMap;
|
551 |
|
552 | #[derive (Debug, Serialize, PartialEq)]
|
553 | struct Unit;
|
554 |
|
555 | #[derive (Debug, Serialize, PartialEq)]
|
556 | struct Newtype(usize);
|
557 |
|
558 | #[derive (Debug, Serialize, PartialEq)]
|
559 | struct Tuple(&'static str, usize);
|
560 |
|
561 | #[derive (Debug, Serialize, PartialEq)]
|
562 | struct Struct {
|
563 | key: &'static str,
|
564 | val: usize,
|
565 | }
|
566 |
|
567 | #[derive (Debug, Serialize, PartialEq)]
|
568 | enum Enum {
|
569 | Unit,
|
570 | #[serde(rename = "< \"&'>" )]
|
571 | UnitEscaped,
|
572 | Newtype(usize),
|
573 | Tuple(&'static str, usize),
|
574 | Struct {
|
575 | key: &'static str,
|
576 | val: usize,
|
577 | },
|
578 | }
|
579 |
|
580 | mod escape_item {
|
581 | use super::*;
|
582 |
|
583 | mod full {
|
584 | use super::*;
|
585 | use pretty_assertions::assert_eq;
|
586 |
|
587 | #[test ]
|
588 | fn text() {
|
589 | assert_eq!(
|
590 | escape_item("text< \"'&> \t\n\rtext" , QuoteTarget::Text, QuoteLevel::Full),
|
591 | "text<"'&> 	 text"
|
592 | );
|
593 | }
|
594 |
|
595 | #[test ]
|
596 | fn double_quote_attr() {
|
597 | assert_eq!(
|
598 | escape_item(
|
599 | "text< \"'&> \t\n\rtext" ,
|
600 | QuoteTarget::DoubleQAttr,
|
601 | QuoteLevel::Full
|
602 | ),
|
603 | "text<"'&> 	 text"
|
604 | );
|
605 | }
|
606 |
|
607 | #[test ]
|
608 | fn single_quote_attr() {
|
609 | assert_eq!(
|
610 | escape_item(
|
611 | "text< \"'&> \t\n\rtext" ,
|
612 | QuoteTarget::SingleQAttr,
|
613 | QuoteLevel::Full
|
614 | ),
|
615 | "text<"'&> 	 text"
|
616 | );
|
617 | }
|
618 | }
|
619 |
|
620 | mod partial {
|
621 | use super::*;
|
622 | use pretty_assertions::assert_eq;
|
623 |
|
624 | #[test ]
|
625 | fn text() {
|
626 | assert_eq!(
|
627 | escape_item(
|
628 | "text< \"'&> \t\n\rtext" ,
|
629 | QuoteTarget::Text,
|
630 | QuoteLevel::Partial
|
631 | ),
|
632 | "text< \"'&> 	 text"
|
633 | );
|
634 | }
|
635 |
|
636 | #[test ]
|
637 | fn double_quote_attr() {
|
638 | assert_eq!(
|
639 | escape_item(
|
640 | "text< \"'&> \t\n\rtext" ,
|
641 | QuoteTarget::DoubleQAttr,
|
642 | QuoteLevel::Partial
|
643 | ),
|
644 | "text<"'&> 	 text"
|
645 | );
|
646 | }
|
647 |
|
648 | #[test ]
|
649 | fn single_quote_attr() {
|
650 | assert_eq!(
|
651 | escape_item(
|
652 | "text< \"'&> \t\n\rtext" ,
|
653 | QuoteTarget::SingleQAttr,
|
654 | QuoteLevel::Partial
|
655 | ),
|
656 | "text< \"'&> 	 text"
|
657 | );
|
658 | }
|
659 | }
|
660 |
|
661 | mod minimal {
|
662 | use super::*;
|
663 | use pretty_assertions::assert_eq;
|
664 |
|
665 | #[test ]
|
666 | fn text() {
|
667 | assert_eq!(
|
668 | escape_item(
|
669 | "text< \"'&> \t\n\rtext" ,
|
670 | QuoteTarget::Text,
|
671 | QuoteLevel::Minimal
|
672 | ),
|
673 | "text< \"'&> 	 text"
|
674 | );
|
675 | }
|
676 |
|
677 | #[test ]
|
678 | fn double_quote_attr() {
|
679 | assert_eq!(
|
680 | escape_item(
|
681 | "text< \"'&> \t\n\rtext" ,
|
682 | QuoteTarget::DoubleQAttr,
|
683 | QuoteLevel::Minimal
|
684 | ),
|
685 | "text<"'&> 	 text"
|
686 | );
|
687 | }
|
688 |
|
689 | #[test ]
|
690 | fn single_quote_attr() {
|
691 | assert_eq!(
|
692 | escape_item(
|
693 | "text< \"'&> \t\n\rtext" ,
|
694 | QuoteTarget::SingleQAttr,
|
695 | QuoteLevel::Minimal
|
696 | ),
|
697 | "text< \"'&> 	 text"
|
698 | );
|
699 | }
|
700 | }
|
701 | }
|
702 |
|
703 | mod escape_list {
|
704 | use super::*;
|
705 |
|
706 | mod full {
|
707 | use super::*;
|
708 | use pretty_assertions::assert_eq;
|
709 |
|
710 | #[test ]
|
711 | fn text() {
|
712 | assert_eq!(
|
713 | escape_list("text< \"'&> \t\n\rtext" , QuoteTarget::Text, QuoteLevel::Full),
|
714 | "text<"'&> \t\n\rtext"
|
715 | );
|
716 | }
|
717 |
|
718 | #[test ]
|
719 | fn double_quote_attr() {
|
720 | assert_eq!(
|
721 | escape_list(
|
722 | "text< \"'&> \t\n\rtext" ,
|
723 | QuoteTarget::DoubleQAttr,
|
724 | QuoteLevel::Full
|
725 | ),
|
726 | "text<"'&> \t\n\rtext"
|
727 | );
|
728 | }
|
729 |
|
730 | #[test ]
|
731 | fn single_quote_attr() {
|
732 | assert_eq!(
|
733 | escape_list(
|
734 | "text< \"'&> \t\n\rtext" ,
|
735 | QuoteTarget::SingleQAttr,
|
736 | QuoteLevel::Full
|
737 | ),
|
738 | "text<"'&> \t\n\rtext"
|
739 | );
|
740 | }
|
741 | }
|
742 |
|
743 | mod partial {
|
744 | use super::*;
|
745 | use pretty_assertions::assert_eq;
|
746 |
|
747 | #[test ]
|
748 | fn text() {
|
749 | assert_eq!(
|
750 | escape_list(
|
751 | "text< \"'&> \t\n\rtext" ,
|
752 | QuoteTarget::Text,
|
753 | QuoteLevel::Partial
|
754 | ),
|
755 | "text< \"'&> \t\n\rtext"
|
756 | );
|
757 | }
|
758 |
|
759 | #[test ]
|
760 | fn double_quote_attr() {
|
761 | assert_eq!(
|
762 | escape_list(
|
763 | "text< \"'&> \t\n\rtext" ,
|
764 | QuoteTarget::DoubleQAttr,
|
765 | QuoteLevel::Partial
|
766 | ),
|
767 | "text<"'&> \t\n\rtext"
|
768 | );
|
769 | }
|
770 |
|
771 | #[test ]
|
772 | fn single_quote_attr() {
|
773 | assert_eq!(
|
774 | escape_list(
|
775 | "text< \"'&> \t\n\rtext" ,
|
776 | QuoteTarget::SingleQAttr,
|
777 | QuoteLevel::Partial
|
778 | ),
|
779 | "text< \"'&> \t\n\rtext"
|
780 | );
|
781 | }
|
782 | }
|
783 |
|
784 | mod minimal {
|
785 | use super::*;
|
786 | use pretty_assertions::assert_eq;
|
787 |
|
788 | #[test ]
|
789 | fn text() {
|
790 | assert_eq!(
|
791 | escape_list(
|
792 | "text< \"'&> \t\n\rtext" ,
|
793 | QuoteTarget::Text,
|
794 | QuoteLevel::Minimal
|
795 | ),
|
796 | "text< \"'&> \t\n\rtext"
|
797 | );
|
798 | }
|
799 |
|
800 | #[test ]
|
801 | fn double_quote_attr() {
|
802 | assert_eq!(
|
803 | escape_list(
|
804 | "text< \"'&> \t\n\rtext" ,
|
805 | QuoteTarget::DoubleQAttr,
|
806 | QuoteLevel::Minimal
|
807 | ),
|
808 | "text<"'&> \t\n\rtext"
|
809 | );
|
810 | }
|
811 |
|
812 | #[test ]
|
813 | fn single_quote_attr() {
|
814 | assert_eq!(
|
815 | escape_list(
|
816 | "text< \"'&> \t\n\rtext" ,
|
817 | QuoteTarget::SingleQAttr,
|
818 | QuoteLevel::Minimal
|
819 | ),
|
820 | "text< \"'&> \t\n\rtext"
|
821 | );
|
822 | }
|
823 | }
|
824 | }
|
825 |
|
826 | /// Tests for serialize atomic and union values, as defined in XSD specification
|
827 | mod atomic {
|
828 | use super::*;
|
829 | use pretty_assertions::assert_eq;
|
830 |
|
831 | /// Checks that given `$data` successfully serialized as `$expected`
|
832 | macro_rules! serialize_as {
|
833 | ($name:ident: $data:expr => $expected:literal) => {
|
834 | #[test]
|
835 | fn $name() {
|
836 | let ser = AtomicSerializer {
|
837 | writer: String::new(),
|
838 | target: QuoteTarget::Text,
|
839 | level: QuoteLevel::Full,
|
840 | };
|
841 |
|
842 | let buffer = $data.serialize(ser).unwrap();
|
843 | assert_eq!(buffer, $expected);
|
844 | }
|
845 | };
|
846 | }
|
847 |
|
848 | /// Checks that attempt to serialize given `$data` results to a
|
849 | /// serialization error `$kind` with `$reason`
|
850 | macro_rules! err {
|
851 | ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
|
852 | #[test]
|
853 | fn $name() {
|
854 | let mut buffer = String::new();
|
855 | let ser = AtomicSerializer {
|
856 | writer: &mut buffer,
|
857 | target: QuoteTarget::Text,
|
858 | level: QuoteLevel::Full,
|
859 | };
|
860 |
|
861 | match $data.serialize(ser).unwrap_err() {
|
862 | DeError::$kind(e) => assert_eq!(e, $reason),
|
863 | e => panic!(
|
864 | "Expected `{}({})`, found `{:?}`" ,
|
865 | stringify!($kind),
|
866 | $reason,
|
867 | e
|
868 | ),
|
869 | }
|
870 | assert_eq!(buffer, "" );
|
871 | }
|
872 | };
|
873 | }
|
874 |
|
875 | serialize_as!(false_: false => "false" );
|
876 | serialize_as!(true_: true => "true" );
|
877 |
|
878 | serialize_as!(i8_: -42i8 => "-42" );
|
879 | serialize_as!(i16_: -4200i16 => "-4200" );
|
880 | serialize_as!(i32_: -42000000i32 => "-42000000" );
|
881 | serialize_as!(i64_: -42000000000000i64 => "-42000000000000" );
|
882 | serialize_as!(isize_: -42000000000000isize => "-42000000000000" );
|
883 |
|
884 | serialize_as!(u8_: 42u8 => "42" );
|
885 | serialize_as!(u16_: 4200u16 => "4200" );
|
886 | serialize_as!(u32_: 42000000u32 => "42000000" );
|
887 | serialize_as!(u64_: 42000000000000u64 => "42000000000000" );
|
888 | serialize_as!(usize_: 42000000000000usize => "42000000000000" );
|
889 |
|
890 | serde_if_integer128! {
|
891 | serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
892 | serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
893 | }
|
894 |
|
895 | serialize_as!(f32_: 4.2f32 => "4.2" );
|
896 | serialize_as!(f64_: 4.2f64 => "4.2" );
|
897 |
|
898 | serialize_as!(char_non_escaped: 'h' => "h" );
|
899 | serialize_as!(char_lt: '<' => "<" );
|
900 | serialize_as!(char_gt: '>' => ">" );
|
901 | serialize_as!(char_amp: '&' => "&" );
|
902 | serialize_as!(char_apos: ' \'' => "'" );
|
903 | serialize_as!(char_quot: '"' => """ );
|
904 |
|
905 | serialize_as!(str_non_escaped: "non-escaped-string" => "non-escaped-string" );
|
906 | serialize_as!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
907 |
|
908 | err!(bytes: Bytes(b"< \"escaped & bytes'>" )
|
909 | => Unsupported("`serialize_bytes` not supported yet" ));
|
910 |
|
911 | serialize_as!(option_none: Option::<&str>::None => "" );
|
912 | serialize_as!(option_some: Some("non-escaped-string" ) => "non-escaped-string" );
|
913 |
|
914 | err!(unit: ()
|
915 | => Unsupported("unit type `()` cannot be serialized as an `xs:list` item" ));
|
916 | err!(unit_struct: Unit
|
917 | => Unsupported("unit struct `Unit` cannot be serialized as an `xs:list` item" ));
|
918 |
|
919 | serialize_as!(enum_unit: Enum::Unit => "Unit" );
|
920 | serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>" );
|
921 |
|
922 | serialize_as!(newtype: Newtype(42) => "42" );
|
923 | err!(enum_newtype: Enum::Newtype(42)
|
924 | => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an `xs:list` item" ));
|
925 |
|
926 | err!(seq: vec![1, 2, 3]
|
927 | => Unsupported("sequence cannot be serialized as an `xs:list` item" ));
|
928 | err!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
929 | => Unsupported("tuple cannot be serialized as an `xs:list` item" ));
|
930 | err!(tuple_struct: Tuple("first" , 42)
|
931 | => Unsupported("tuple struct `Tuple` cannot be serialized as an `xs:list` item" ));
|
932 | err!(enum_tuple: Enum::Tuple("first" , 42)
|
933 | => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an `xs:list` item" ));
|
934 |
|
935 | err!(map: BTreeMap::from([(1, 2), (3, 4)])
|
936 | => Unsupported("map cannot be serialized as an `xs:list` item" ));
|
937 | err!(struct_: Struct { key: "answer" , val: 42 }
|
938 | => Unsupported("struct `Struct` cannot be serialized as an `xs:list` item" ));
|
939 | err!(enum_struct: Enum::Struct { key: "answer" , val: 42 }
|
940 | => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an `xs:list` item" ));
|
941 | }
|
942 |
|
943 | mod simple_type {
|
944 | use super::*;
|
945 | use pretty_assertions::assert_eq;
|
946 |
|
947 | /// Checks that given `$data` successfully serialized as `$expected`
|
948 | macro_rules! serialize_as {
|
949 | ($name:ident: $data:expr => $expected:literal) => {
|
950 | #[test]
|
951 | fn $name() {
|
952 | let ser = SimpleTypeSerializer {
|
953 | writer: String::new(),
|
954 | target: QuoteTarget::Text,
|
955 | level: QuoteLevel::Full,
|
956 | indent: Indent::None,
|
957 | };
|
958 |
|
959 | let buffer = $data.serialize(ser).unwrap();
|
960 | assert_eq!(buffer, $expected);
|
961 | }
|
962 | };
|
963 | }
|
964 |
|
965 | /// Checks that attempt to serialize given `$data` results to a
|
966 | /// serialization error `$kind` with `$reason`
|
967 | macro_rules! err {
|
968 | ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
|
969 | #[test]
|
970 | fn $name() {
|
971 | let mut buffer = String::new();
|
972 | let ser = SimpleTypeSerializer {
|
973 | writer: &mut buffer,
|
974 | target: QuoteTarget::Text,
|
975 | level: QuoteLevel::Full,
|
976 | indent: Indent::None,
|
977 | };
|
978 |
|
979 | match $data.serialize(ser).unwrap_err() {
|
980 | DeError::$kind(e) => assert_eq!(e, $reason),
|
981 | e => panic!(
|
982 | "Expected `{}({})`, found `{:?}`" ,
|
983 | stringify!($kind),
|
984 | $reason,
|
985 | e
|
986 | ),
|
987 | }
|
988 | assert_eq!(buffer, "" );
|
989 | }
|
990 | };
|
991 | }
|
992 |
|
993 | serialize_as!(false_: false => "false" );
|
994 | serialize_as!(true_: true => "true" );
|
995 |
|
996 | serialize_as!(i8_: -42i8 => "-42" );
|
997 | serialize_as!(i16_: -4200i16 => "-4200" );
|
998 | serialize_as!(i32_: -42000000i32 => "-42000000" );
|
999 | serialize_as!(i64_: -42000000000000i64 => "-42000000000000" );
|
1000 | serialize_as!(isize_: -42000000000000isize => "-42000000000000" );
|
1001 |
|
1002 | serialize_as!(u8_: 42u8 => "42" );
|
1003 | serialize_as!(u16_: 4200u16 => "4200" );
|
1004 | serialize_as!(u32_: 42000000u32 => "42000000" );
|
1005 | serialize_as!(u64_: 42000000000000u64 => "42000000000000" );
|
1006 | serialize_as!(usize_: 42000000000000usize => "42000000000000" );
|
1007 |
|
1008 | serde_if_integer128! {
|
1009 | serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
1010 | serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
1011 | }
|
1012 |
|
1013 | serialize_as!(f32_: 4.2f32 => "4.2" );
|
1014 | serialize_as!(f64_: 4.2f64 => "4.2" );
|
1015 |
|
1016 | serialize_as!(char_non_escaped: 'h' => "h" );
|
1017 | serialize_as!(char_lt: '<' => "<" );
|
1018 | serialize_as!(char_gt: '>' => ">" );
|
1019 | serialize_as!(char_amp: '&' => "&" );
|
1020 | serialize_as!(char_apos: ' \'' => "'" );
|
1021 | serialize_as!(char_quot: '"' => """ );
|
1022 |
|
1023 | serialize_as!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
1024 | serialize_as!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
1025 |
|
1026 | err!(bytes: Bytes(b"< \"escaped & bytes'>" )
|
1027 | => Unsupported("`serialize_bytes` not supported yet" ));
|
1028 |
|
1029 | serialize_as!(option_none: Option::<&str>::None => "" );
|
1030 | serialize_as!(option_some: Some("non-escaped string" ) => "non-escaped string" );
|
1031 |
|
1032 | serialize_as!(unit: () => "" );
|
1033 | serialize_as!(unit_struct: Unit => "" );
|
1034 |
|
1035 | serialize_as!(enum_unit: Enum::Unit => "Unit" );
|
1036 | serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "<"&'>" );
|
1037 |
|
1038 | serialize_as!(newtype: Newtype(42) => "42" );
|
1039 | err!(enum_newtype: Enum::Newtype(42)
|
1040 | => Unsupported("enum newtype variant `Enum::Newtype` cannot be serialized as an attribute or text content value" ));
|
1041 |
|
1042 | serialize_as!(seq: vec![1, 2, 3] => "1 2 3" );
|
1043 | serialize_as!(seq_empty: Vec::<usize>::new() => "" );
|
1044 | serialize_as!(seq_with_1_empty_str: vec!["" ] => "" );
|
1045 | serialize_as!(seq_with_2_empty_strs: vec!["" , "" ] => " " );
|
1046 | serialize_as!(tuple: ("< \"&'>" , "with \t\n\r spaces" , 3usize)
|
1047 | => "<"&'> with	  spaces 3" );
|
1048 | serialize_as!(tuple_struct: Tuple("first" , 42) => "first 42" );
|
1049 | err!(enum_tuple: Enum::Tuple("first" , 42)
|
1050 | => Unsupported("enum tuple variant `Enum::Tuple` cannot be serialized as an attribute or text content value" ));
|
1051 |
|
1052 | err!(map: BTreeMap::from([(1, 2), (3, 4)])
|
1053 | => Unsupported("map cannot be serialized as an attribute or text content value" ));
|
1054 | err!(struct_: Struct { key: "answer" , val: 42 }
|
1055 | => Unsupported("struct `Struct` cannot be serialized as an attribute or text content value" ));
|
1056 | err!(enum_struct: Enum::Struct { key: "answer" , val: 42 }
|
1057 | => Unsupported("enum struct variant `Enum::Struct` cannot be serialized as an attribute or text content value" ));
|
1058 | }
|
1059 | }
|
1060 | |