| 1 | //! Contains serializer for content of an XML element
|
| 2 |
|
| 3 | use crate::errors::serialize::DeError;
|
| 4 | use crate::se::element::{ElementSerializer, Struct, Tuple};
|
| 5 | use crate::se::simple_type::{QuoteTarget, SimpleTypeSerializer};
|
| 6 | use crate::se::{Indent, QuoteLevel, XmlName};
|
| 7 | use serde::ser::{
|
| 8 | Impossible, Serialize, SerializeSeq, SerializeTuple, SerializeTupleStruct, Serializer,
|
| 9 | };
|
| 10 | use serde::serde_if_integer128;
|
| 11 | use std::fmt::Write;
|
| 12 |
|
| 13 | macro_rules! write_primitive {
|
| 14 | ($method:ident ( $ty:ty )) => {
|
| 15 | #[inline]
|
| 16 | fn $method(self, value: $ty) -> Result<Self::Ok, Self::Error> {
|
| 17 | self.into_simple_type_serializer().$method(value)?;
|
| 18 | Ok(())
|
| 19 | }
|
| 20 | };
|
| 21 | }
|
| 22 |
|
| 23 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
| 24 |
|
| 25 | /// A serializer used to serialize content of the element. It does not write
|
| 26 | /// surrounding tags.
|
| 27 | ///
|
| 28 | /// This serializer does the following:
|
| 29 | /// - primitives (booleans, numbers and strings) serialized as naked strings
|
| 30 | /// - `None` does not write anything
|
| 31 | /// - sequences serialized without delimiters. `[1, 2, 3]` would be serialized as `123`
|
| 32 | /// - units (`()`) and unit structs are not supported
|
| 33 | /// - structs and maps are not supported
|
| 34 | /// - unit variants serialized as self-closed `<${variant}/>`
|
| 35 | /// - tuple variants serialized as sequences where each is wrapped in
|
| 36 | /// `<${variant}>...</${variant}>`
|
| 37 | /// - struct variants serialized wrapped `<${variant}>...</${variant}>`
|
| 38 | ///
|
| 39 | /// The difference between this serializer and [`SimpleTypeSerializer`] is in how
|
| 40 | /// sequences and maps are serialized. Unlike `SimpleTypeSerializer` it supports
|
| 41 | /// any types in sequences and serializes them as list of elements, but that has
|
| 42 | /// drawbacks. Sequence of primitives would be serialized without delimiters and
|
| 43 | /// it will be impossible to distinguish between them. Even worse, when serializing
|
| 44 | /// with indent, sequence of strings become one big string with additional content
|
| 45 | /// and it would be impossible to distinguish between content of the original
|
| 46 | /// strings and inserted indent characters.
|
| 47 | pub struct ContentSerializer<'w, 'i, W: Write> {
|
| 48 | pub writer: &'w mut W,
|
| 49 | /// Defines which XML characters need to be escaped in text content
|
| 50 | pub level: QuoteLevel,
|
| 51 | /// Current indentation level. Note, that `Indent::None` means that there is
|
| 52 | /// no indentation at all, but `write_indent == false` means only, that indent
|
| 53 | /// writing is disabled in this instantiation of `ContentSerializer`, but
|
| 54 | /// child serializers should have access to the actual state of indentation.
|
| 55 | pub(super) indent: Indent<'i>,
|
| 56 | /// If `true`, then current indent will be written before writing the content,
|
| 57 | /// but only if content is not empty.
|
| 58 | pub write_indent: bool,
|
| 59 | // If `true`, then empty elements will be serialized as `<element></element>`
|
| 60 | // instead of `<element/>`.
|
| 61 | pub expand_empty_elements: bool,
|
| 62 | //TODO: add settings to disallow consequent serialization of primitives
|
| 63 | }
|
| 64 |
|
| 65 | impl<'w, 'i, W: Write> ContentSerializer<'w, 'i, W> {
|
| 66 | /// Turns this serializer into serializer of a text content
|
| 67 | #[inline ]
|
| 68 | pub fn into_simple_type_serializer(self) -> SimpleTypeSerializer<'i, &'w mut W> {
|
| 69 | //TODO: Customization point: choose between CDATA and Text representation
|
| 70 | SimpleTypeSerializer {
|
| 71 | writer: self.writer,
|
| 72 | target: QuoteTarget::Text,
|
| 73 | level: self.level,
|
| 74 | indent: if self.write_indent {
|
| 75 | self.indent
|
| 76 | } else {
|
| 77 | Indent::None
|
| 78 | },
|
| 79 | }
|
| 80 | }
|
| 81 |
|
| 82 | /// Creates new serializer that shares state with this serializer and
|
| 83 | /// writes to the same underlying writer
|
| 84 | #[inline ]
|
| 85 | pub fn new_seq_element_serializer(&mut self) -> ContentSerializer<W> {
|
| 86 | ContentSerializer {
|
| 87 | writer: self.writer,
|
| 88 | level: self.level,
|
| 89 | indent: self.indent.borrow(),
|
| 90 | write_indent: self.write_indent,
|
| 91 | expand_empty_elements: self.expand_empty_elements,
|
| 92 | }
|
| 93 | }
|
| 94 |
|
| 95 | /// Writes `name` as self-closed tag
|
| 96 | #[inline ]
|
| 97 | pub(super) fn write_empty(mut self, name: XmlName) -> Result<(), DeError> {
|
| 98 | self.write_indent()?;
|
| 99 | if self.expand_empty_elements {
|
| 100 | self.writer.write_char('<' )?;
|
| 101 | self.writer.write_str(name.0)?;
|
| 102 | self.writer.write_str("></" )?;
|
| 103 | self.writer.write_str(name.0)?;
|
| 104 | self.writer.write_char('>' )?;
|
| 105 | } else {
|
| 106 | self.writer.write_str("<" )?;
|
| 107 | self.writer.write_str(name.0)?;
|
| 108 | self.writer.write_str("/>" )?;
|
| 109 | }
|
| 110 | Ok(())
|
| 111 | }
|
| 112 |
|
| 113 | /// Writes simple type content between `name` tags
|
| 114 | pub(super) fn write_wrapped<S>(mut self, name: XmlName, serialize: S) -> Result<(), DeError>
|
| 115 | where
|
| 116 | S: for<'a> FnOnce(SimpleTypeSerializer<'i, &'a mut W>) -> Result<&'a mut W, DeError>,
|
| 117 | {
|
| 118 | self.write_indent()?;
|
| 119 | self.writer.write_char('<' )?;
|
| 120 | self.writer.write_str(name.0)?;
|
| 121 | self.writer.write_char('>' )?;
|
| 122 |
|
| 123 | let writer = serialize(self.into_simple_type_serializer())?;
|
| 124 |
|
| 125 | writer.write_str("</" )?;
|
| 126 | writer.write_str(name.0)?;
|
| 127 | writer.write_char('>' )?;
|
| 128 | Ok(())
|
| 129 | }
|
| 130 |
|
| 131 | pub(super) fn write_indent(&mut self) -> Result<(), DeError> {
|
| 132 | if self.write_indent {
|
| 133 | self.indent.write_indent(&mut self.writer)?;
|
| 134 | self.write_indent = false;
|
| 135 | }
|
| 136 | Ok(())
|
| 137 | }
|
| 138 | }
|
| 139 |
|
| 140 | impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> {
|
| 141 | type Ok = ();
|
| 142 | type Error = DeError;
|
| 143 |
|
| 144 | type SerializeSeq = Self;
|
| 145 | type SerializeTuple = Self;
|
| 146 | type SerializeTupleStruct = Self;
|
| 147 | type SerializeTupleVariant = Tuple<'w, 'i, W>;
|
| 148 | type SerializeMap = Impossible<Self::Ok, Self::Error>;
|
| 149 | type SerializeStruct = Impossible<Self::Ok, Self::Error>;
|
| 150 | type SerializeStructVariant = Struct<'w, 'i, W>;
|
| 151 |
|
| 152 | write_primitive!(serialize_bool(bool));
|
| 153 |
|
| 154 | write_primitive!(serialize_i8(i8));
|
| 155 | write_primitive!(serialize_i16(i16));
|
| 156 | write_primitive!(serialize_i32(i32));
|
| 157 | write_primitive!(serialize_i64(i64));
|
| 158 |
|
| 159 | write_primitive!(serialize_u8(u8));
|
| 160 | write_primitive!(serialize_u16(u16));
|
| 161 | write_primitive!(serialize_u32(u32));
|
| 162 | write_primitive!(serialize_u64(u64));
|
| 163 |
|
| 164 | serde_if_integer128! {
|
| 165 | write_primitive!(serialize_i128(i128));
|
| 166 | write_primitive!(serialize_u128(u128));
|
| 167 | }
|
| 168 |
|
| 169 | write_primitive!(serialize_f32(f32));
|
| 170 | write_primitive!(serialize_f64(f64));
|
| 171 |
|
| 172 | write_primitive!(serialize_char(char));
|
| 173 | write_primitive!(serialize_bytes(&[u8]));
|
| 174 |
|
| 175 | #[inline ]
|
| 176 | fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {
|
| 177 | if !value.is_empty() {
|
| 178 | self.into_simple_type_serializer().serialize_str(value)?;
|
| 179 | }
|
| 180 | Ok(())
|
| 181 | }
|
| 182 |
|
| 183 | /// Does not write anything
|
| 184 | #[inline ]
|
| 185 | fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
| 186 | Ok(())
|
| 187 | }
|
| 188 |
|
| 189 | fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, Self::Error> {
|
| 190 | value.serialize(self)
|
| 191 | }
|
| 192 |
|
| 193 | /// Does not write anything
|
| 194 | #[inline ]
|
| 195 | fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
| 196 | Ok(())
|
| 197 | }
|
| 198 |
|
| 199 | /// Does not write anything
|
| 200 | #[inline ]
|
| 201 | fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
|
| 202 | Ok(())
|
| 203 | }
|
| 204 |
|
| 205 | /// Checks `variant` for XML name validity and writes `<${variant}/>`
|
| 206 | fn serialize_unit_variant(
|
| 207 | self,
|
| 208 | _name: &'static str,
|
| 209 | _variant_index: u32,
|
| 210 | variant: &'static str,
|
| 211 | ) -> Result<Self::Ok, Self::Error> {
|
| 212 | let name = XmlName::try_from(variant)?;
|
| 213 | self.write_empty(name)
|
| 214 | }
|
| 215 |
|
| 216 | fn serialize_newtype_struct<T: ?Sized + Serialize>(
|
| 217 | self,
|
| 218 | _name: &'static str,
|
| 219 | value: &T,
|
| 220 | ) -> Result<Self::Ok, Self::Error> {
|
| 221 | value.serialize(self)
|
| 222 | }
|
| 223 |
|
| 224 | /// Checks `variant` for XML name validity and writes `value` as new element
|
| 225 | /// with name `variant`.
|
| 226 | fn serialize_newtype_variant<T: ?Sized + Serialize>(
|
| 227 | self,
|
| 228 | _name: &'static str,
|
| 229 | _variant_index: u32,
|
| 230 | variant: &'static str,
|
| 231 | value: &T,
|
| 232 | ) -> Result<Self::Ok, Self::Error> {
|
| 233 | value.serialize(ElementSerializer {
|
| 234 | key: XmlName::try_from(variant)?,
|
| 235 | ser: self,
|
| 236 | })
|
| 237 | }
|
| 238 |
|
| 239 | #[inline ]
|
| 240 | fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
| 241 | Ok(self)
|
| 242 | }
|
| 243 |
|
| 244 | #[inline ]
|
| 245 | fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
| 246 | self.serialize_seq(Some(len))
|
| 247 | }
|
| 248 |
|
| 249 | #[inline ]
|
| 250 | fn serialize_tuple_struct(
|
| 251 | self,
|
| 252 | _name: &'static str,
|
| 253 | len: usize,
|
| 254 | ) -> Result<Self::SerializeTupleStruct, Self::Error> {
|
| 255 | self.serialize_tuple(len)
|
| 256 | }
|
| 257 |
|
| 258 | #[inline ]
|
| 259 | fn serialize_tuple_variant(
|
| 260 | self,
|
| 261 | name: &'static str,
|
| 262 | _variant_index: u32,
|
| 263 | variant: &'static str,
|
| 264 | len: usize,
|
| 265 | ) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
| 266 | let ser = ElementSerializer {
|
| 267 | key: XmlName::try_from(variant)?,
|
| 268 | ser: self,
|
| 269 | };
|
| 270 | // `ElementSerializer::serialize_tuple_variant` is the same as
|
| 271 | // `ElementSerializer::serialize_tuple_struct`, except that it replaces `.key`
|
| 272 | // to `variant` which is not required here
|
| 273 | ser.serialize_tuple_struct(name, len).map(Tuple::Element)
|
| 274 | }
|
| 275 |
|
| 276 | fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
| 277 | Err(DeError::Unsupported(
|
| 278 | format!("serialization of map types is not supported in `$value` field" ).into(),
|
| 279 | ))
|
| 280 | }
|
| 281 |
|
| 282 | #[inline ]
|
| 283 | fn serialize_struct(
|
| 284 | self,
|
| 285 | name: &'static str,
|
| 286 | _len: usize,
|
| 287 | ) -> Result<Self::SerializeStruct, Self::Error> {
|
| 288 | Err(DeError::Unsupported(
|
| 289 | format!("serialization of struct ` {name}` is not supported in `$value` field" ).into(),
|
| 290 | ))
|
| 291 | }
|
| 292 |
|
| 293 | #[inline ]
|
| 294 | fn serialize_struct_variant(
|
| 295 | self,
|
| 296 | name: &'static str,
|
| 297 | _variant_index: u32,
|
| 298 | variant: &'static str,
|
| 299 | len: usize,
|
| 300 | ) -> Result<Self::SerializeStructVariant, Self::Error> {
|
| 301 | let ser = ElementSerializer {
|
| 302 | key: XmlName::try_from(variant)?,
|
| 303 | ser: self,
|
| 304 | };
|
| 305 | // `ElementSerializer::serialize_struct_variant` is the same as
|
| 306 | // `ElementSerializer::serialize_struct`, except that it replaces `.key`
|
| 307 | // to `variant` which is not required here
|
| 308 | ser.serialize_struct(name, len)
|
| 309 | }
|
| 310 | }
|
| 311 |
|
| 312 | impl<'w, 'i, W: Write> SerializeSeq for ContentSerializer<'w, 'i, W> {
|
| 313 | type Ok = ();
|
| 314 | type Error = DeError;
|
| 315 |
|
| 316 | fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
| 317 | where
|
| 318 | T: ?Sized + Serialize,
|
| 319 | {
|
| 320 | value.serialize(self.new_seq_element_serializer())?;
|
| 321 | // Write indent for next element
|
| 322 | self.write_indent = true;
|
| 323 | Ok(())
|
| 324 | }
|
| 325 |
|
| 326 | #[inline ]
|
| 327 | fn end(self) -> Result<Self::Ok, Self::Error> {
|
| 328 | Ok(())
|
| 329 | }
|
| 330 | }
|
| 331 |
|
| 332 | impl<'w, 'i, W: Write> SerializeTuple for ContentSerializer<'w, 'i, W> {
|
| 333 | type Ok = ();
|
| 334 | type Error = DeError;
|
| 335 |
|
| 336 | #[inline ]
|
| 337 | fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
| 338 | where
|
| 339 | T: ?Sized + Serialize,
|
| 340 | {
|
| 341 | <Self as SerializeSeq>::serialize_element(self, value)
|
| 342 | }
|
| 343 |
|
| 344 | #[inline ]
|
| 345 | fn end(self) -> Result<Self::Ok, Self::Error> {
|
| 346 | <Self as SerializeSeq>::end(self)
|
| 347 | }
|
| 348 | }
|
| 349 |
|
| 350 | impl<'w, 'i, W: Write> SerializeTupleStruct for ContentSerializer<'w, 'i, W> {
|
| 351 | type Ok = ();
|
| 352 | type Error = DeError;
|
| 353 |
|
| 354 | #[inline ]
|
| 355 | fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
| 356 | where
|
| 357 | T: ?Sized + Serialize,
|
| 358 | {
|
| 359 | <Self as SerializeSeq>::serialize_element(self, value)
|
| 360 | }
|
| 361 |
|
| 362 | #[inline ]
|
| 363 | fn end(self) -> Result<Self::Ok, Self::Error> {
|
| 364 | <Self as SerializeSeq>::end(self)
|
| 365 | }
|
| 366 | }
|
| 367 |
|
| 368 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
| 369 |
|
| 370 | /// Make tests public to reuse types in `elements::tests` module
|
| 371 | #[cfg (test)]
|
| 372 | pub(super) mod tests {
|
| 373 | use super::*;
|
| 374 | use crate::utils::Bytes;
|
| 375 | use serde::Serialize;
|
| 376 | use std::collections::BTreeMap;
|
| 377 |
|
| 378 | #[derive (Debug, Serialize, PartialEq)]
|
| 379 | pub struct Unit;
|
| 380 |
|
| 381 | #[derive (Debug, Serialize, PartialEq)]
|
| 382 | #[serde(rename = "< \"&'>" )]
|
| 383 | pub struct UnitEscaped;
|
| 384 |
|
| 385 | #[derive (Debug, Serialize, PartialEq)]
|
| 386 | pub struct Newtype(pub usize);
|
| 387 |
|
| 388 | #[derive (Debug, Serialize, PartialEq)]
|
| 389 | pub struct Tuple(pub &'static str, pub usize);
|
| 390 |
|
| 391 | #[derive (Debug, Serialize, PartialEq)]
|
| 392 | pub struct Struct {
|
| 393 | pub key: &'static str,
|
| 394 | pub val: (usize, usize),
|
| 395 | }
|
| 396 |
|
| 397 | #[derive (Debug, Serialize, PartialEq)]
|
| 398 | pub struct Text<T> {
|
| 399 | pub before: &'static str,
|
| 400 | #[serde(rename = "$text" )]
|
| 401 | pub content: T,
|
| 402 | pub after: &'static str,
|
| 403 | }
|
| 404 |
|
| 405 | #[derive (Debug, Serialize, PartialEq)]
|
| 406 | pub struct Value<T> {
|
| 407 | pub before: &'static str,
|
| 408 | #[serde(rename = "$value" )]
|
| 409 | pub content: T,
|
| 410 | pub after: &'static str,
|
| 411 | }
|
| 412 |
|
| 413 | /// Attributes identified by starting with `@` character
|
| 414 | #[derive (Debug, Serialize, PartialEq)]
|
| 415 | pub struct Attributes {
|
| 416 | #[serde(rename = "@key" )]
|
| 417 | pub key: &'static str,
|
| 418 | #[serde(rename = "@val" )]
|
| 419 | pub val: (usize, usize),
|
| 420 | }
|
| 421 | #[derive (Debug, Serialize, PartialEq)]
|
| 422 | pub struct AttributesBefore {
|
| 423 | #[serde(rename = "@key" )]
|
| 424 | pub key: &'static str,
|
| 425 | pub val: usize,
|
| 426 | }
|
| 427 | #[derive (Debug, Serialize, PartialEq)]
|
| 428 | pub struct AttributesAfter {
|
| 429 | pub key: &'static str,
|
| 430 | #[serde(rename = "@val" )]
|
| 431 | pub val: usize,
|
| 432 | }
|
| 433 |
|
| 434 | #[derive (Debug, Serialize, PartialEq)]
|
| 435 | pub enum Enum {
|
| 436 | Unit,
|
| 437 | /// Variant name becomes a tag name, but the name of variant is invalid
|
| 438 | /// XML name. Serialization of this element should be forbidden
|
| 439 | #[serde(rename = "< \"&'>" )]
|
| 440 | UnitEscaped,
|
| 441 | Newtype(usize),
|
| 442 | Tuple(&'static str, usize),
|
| 443 | Struct {
|
| 444 | key: &'static str,
|
| 445 | /// Should be serialized as elements
|
| 446 | val: (usize, usize),
|
| 447 | },
|
| 448 | Attributes {
|
| 449 | #[serde(rename = "@key" )]
|
| 450 | key: &'static str,
|
| 451 | #[serde(rename = "@val" )]
|
| 452 | val: (usize, usize),
|
| 453 | },
|
| 454 | AttributesBefore {
|
| 455 | #[serde(rename = "@key" )]
|
| 456 | key: &'static str,
|
| 457 | val: usize,
|
| 458 | },
|
| 459 | AttributesAfter {
|
| 460 | key: &'static str,
|
| 461 | #[serde(rename = "@val" )]
|
| 462 | val: usize,
|
| 463 | },
|
| 464 | }
|
| 465 |
|
| 466 | #[derive (Debug, Serialize, PartialEq)]
|
| 467 | pub enum SpecialEnum<T> {
|
| 468 | Text {
|
| 469 | before: &'static str,
|
| 470 | #[serde(rename = "$text" )]
|
| 471 | content: T,
|
| 472 | after: &'static str,
|
| 473 | },
|
| 474 | Value {
|
| 475 | before: &'static str,
|
| 476 | #[serde(rename = "$value" )]
|
| 477 | content: T,
|
| 478 | after: &'static str,
|
| 479 | },
|
| 480 | }
|
| 481 |
|
| 482 | mod without_indent {
|
| 483 | use super::Struct;
|
| 484 | use super::*;
|
| 485 | use pretty_assertions::assert_eq;
|
| 486 |
|
| 487 | /// Checks that given `$data` successfully serialized as `$expected`
|
| 488 | macro_rules! serialize_as {
|
| 489 | ($name:ident: $data:expr => $expected:literal) => {
|
| 490 | #[test]
|
| 491 | fn $name() {
|
| 492 | let mut buffer = String::new();
|
| 493 | let ser = ContentSerializer {
|
| 494 | writer: &mut buffer,
|
| 495 | level: QuoteLevel::Full,
|
| 496 | indent: Indent::None,
|
| 497 | write_indent: false,
|
| 498 | expand_empty_elements: false,
|
| 499 | };
|
| 500 |
|
| 501 | $data.serialize(ser).unwrap();
|
| 502 | assert_eq!(buffer, $expected);
|
| 503 | }
|
| 504 | };
|
| 505 | }
|
| 506 |
|
| 507 | /// Checks that attempt to serialize given `$data` results to a
|
| 508 | /// serialization error `$kind` with `$reason`
|
| 509 | macro_rules! err {
|
| 510 | ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
|
| 511 | #[test]
|
| 512 | fn $name() {
|
| 513 | let mut buffer = String::new();
|
| 514 | let ser = ContentSerializer {
|
| 515 | writer: &mut buffer,
|
| 516 | level: QuoteLevel::Full,
|
| 517 | indent: Indent::None,
|
| 518 | write_indent: false,
|
| 519 | expand_empty_elements: false,
|
| 520 | };
|
| 521 |
|
| 522 | match $data.serialize(ser).unwrap_err() {
|
| 523 | DeError::$kind(e) => assert_eq!(e, $reason),
|
| 524 | e => panic!(
|
| 525 | "Expected `{}({})`, found `{:?}`" ,
|
| 526 | stringify!($kind),
|
| 527 | $reason,
|
| 528 | e
|
| 529 | ),
|
| 530 | }
|
| 531 | // We could write something before fail
|
| 532 | // assert_eq!(buffer, "");
|
| 533 | }
|
| 534 | };
|
| 535 | }
|
| 536 |
|
| 537 | // Primitives is serialized in the same way as for SimpleTypeSerializer
|
| 538 | serialize_as!(false_: false => "false" );
|
| 539 | serialize_as!(true_: true => "true" );
|
| 540 |
|
| 541 | serialize_as!(i8_: -42i8 => "-42" );
|
| 542 | serialize_as!(i16_: -4200i16 => "-4200" );
|
| 543 | serialize_as!(i32_: -42000000i32 => "-42000000" );
|
| 544 | serialize_as!(i64_: -42000000000000i64 => "-42000000000000" );
|
| 545 | serialize_as!(isize_: -42000000000000isize => "-42000000000000" );
|
| 546 |
|
| 547 | serialize_as!(u8_: 42u8 => "42" );
|
| 548 | serialize_as!(u16_: 4200u16 => "4200" );
|
| 549 | serialize_as!(u32_: 42000000u32 => "42000000" );
|
| 550 | serialize_as!(u64_: 42000000000000u64 => "42000000000000" );
|
| 551 | serialize_as!(usize_: 42000000000000usize => "42000000000000" );
|
| 552 |
|
| 553 | serde_if_integer128! {
|
| 554 | serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
| 555 | serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
| 556 | }
|
| 557 |
|
| 558 | serialize_as!(f32_: 4.2f32 => "4.2" );
|
| 559 | serialize_as!(f64_: 4.2f64 => "4.2" );
|
| 560 |
|
| 561 | serialize_as!(char_non_escaped: 'h' => "h" );
|
| 562 | serialize_as!(char_lt: '<' => "<" );
|
| 563 | serialize_as!(char_gt: '>' => ">" );
|
| 564 | serialize_as!(char_amp: '&' => "&" );
|
| 565 | serialize_as!(char_apos: ' \'' => "'" );
|
| 566 | serialize_as!(char_quot: '"' => """ );
|
| 567 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
| 568 | // pretty-print does not change the content
|
| 569 | serialize_as!(char_space: ' ' => " " );
|
| 570 |
|
| 571 | serialize_as!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
| 572 | serialize_as!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
| 573 |
|
| 574 | err!(bytes: Bytes(b"< \"escaped & bytes'>" ) => Unsupported("`serialize_bytes` not supported yet" ));
|
| 575 |
|
| 576 | serialize_as!(option_none: Option::<Enum>::None => "" );
|
| 577 | serialize_as!(option_some: Some("non-escaped string" ) => "non-escaped string" );
|
| 578 | serialize_as!(option_some_empty_str: Some("" ) => "" );
|
| 579 |
|
| 580 | serialize_as!(unit: () => "" );
|
| 581 | serialize_as!(unit_struct: Unit => "" );
|
| 582 | serialize_as!(unit_struct_escaped: UnitEscaped => "" );
|
| 583 |
|
| 584 | // Unlike SimpleTypeSerializer, enumeration values serialized as tags
|
| 585 | serialize_as!(enum_unit: Enum::Unit => "<Unit/>" );
|
| 586 | err!(enum_unit_escaped: Enum::UnitEscaped
|
| 587 | => Unsupported("character `<` is not allowed at the start of an XML name `< \"&'>`" ));
|
| 588 |
|
| 589 | // Newtypes recursively applies ContentSerializer
|
| 590 | serialize_as!(newtype: Newtype(42) => "42" );
|
| 591 | serialize_as!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>" );
|
| 592 |
|
| 593 | // Note that sequences of primitives serialized without delimiters!
|
| 594 | serialize_as!(seq: vec![1, 2, 3] => "123" );
|
| 595 | serialize_as!(seq_empty: Vec::<usize>::new() => "" );
|
| 596 | serialize_as!(tuple: ("< \"&'>" , "with \t\r\n spaces" , 3usize)
|
| 597 | => "<"&'> \
|
| 598 | with \t\r\n spaces \
|
| 599 | 3" );
|
| 600 | serialize_as!(tuple_struct: Tuple("first" , 42)
|
| 601 | => "first \
|
| 602 | 42" );
|
| 603 | serialize_as!(enum_tuple: Enum::Tuple("first" , 42)
|
| 604 | => "<Tuple>first</Tuple> \
|
| 605 | <Tuple>42</Tuple>" );
|
| 606 |
|
| 607 | // Structured types cannot be serialized without surrounding tag, which
|
| 608 | // only `enum` can provide
|
| 609 | err!(map: BTreeMap::from([("_1" , 2), ("_3" , 4)])
|
| 610 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
| 611 | err!(struct_: Struct { key: "answer" , val: (42, 42) }
|
| 612 | => Unsupported("serialization of struct `Struct` is not supported in `$value` field" ));
|
| 613 | serialize_as!(enum_struct: Enum::Struct { key: "answer" , val: (42, 42) }
|
| 614 | => "<Struct> \
|
| 615 | <key>answer</key> \
|
| 616 | <val>42</val> \
|
| 617 | <val>42</val> \
|
| 618 | </Struct>" );
|
| 619 |
|
| 620 | /// Special field name `$text` should be serialized as a text content
|
| 621 | mod text {
|
| 622 | use super::*;
|
| 623 | use pretty_assertions::assert_eq;
|
| 624 |
|
| 625 | err!(map: BTreeMap::from([("$text" , 2), ("_3" , 4)])
|
| 626 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
| 627 | err!(struct_:
|
| 628 | Text {
|
| 629 | before: "answer" ,
|
| 630 | content: (42, 42),
|
| 631 | after: "answer" ,
|
| 632 | }
|
| 633 | => Unsupported("serialization of struct `Text` is not supported in `$value` field" ));
|
| 634 | serialize_as!(enum_struct:
|
| 635 | SpecialEnum::Text {
|
| 636 | before: "answer" ,
|
| 637 | content: (42, 42),
|
| 638 | after: "answer" ,
|
| 639 | }
|
| 640 | => "<Text> \
|
| 641 | <before>answer</before> \
|
| 642 | 42 42 \
|
| 643 | <after>answer</after> \
|
| 644 | </Text>" );
|
| 645 | }
|
| 646 |
|
| 647 | mod attributes {
|
| 648 | use super::*;
|
| 649 | use pretty_assertions::assert_eq;
|
| 650 |
|
| 651 | err!(map_attr: BTreeMap::from([("@key1" , 1), ("@key2" , 2)])
|
| 652 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
| 653 | err!(map_mixed: BTreeMap::from([("@key1" , 1), ("key2" , 2)])
|
| 654 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
| 655 |
|
| 656 | err!(struct_: Attributes { key: "answer" , val: (42, 42) }
|
| 657 | => Unsupported("serialization of struct `Attributes` is not supported in `$value` field" ));
|
| 658 | err!(struct_before: AttributesBefore { key: "answer" , val: 42 }
|
| 659 | => Unsupported("serialization of struct `AttributesBefore` is not supported in `$value` field" ));
|
| 660 | err!(struct_after: AttributesAfter { key: "answer" , val: 42 }
|
| 661 | => Unsupported("serialization of struct `AttributesAfter` is not supported in `$value` field" ));
|
| 662 |
|
| 663 | serialize_as!(enum_: Enum::Attributes { key: "answer" , val: (42, 42) }
|
| 664 | => r#"<Attributes key="answer" val="42 42"/>"# );
|
| 665 | serialize_as!(enum_before: Enum::AttributesBefore { key: "answer" , val: 42 }
|
| 666 | => r#"<AttributesBefore key="answer"><val>42</val></AttributesBefore>"# );
|
| 667 | serialize_as!(enum_after: Enum::AttributesAfter { key: "answer" , val: 42 }
|
| 668 | => r#"<AttributesAfter val="42"><key>answer</key></AttributesAfter>"# );
|
| 669 | }
|
| 670 | }
|
| 671 |
|
| 672 | mod with_indent {
|
| 673 | use super::Struct;
|
| 674 | use super::*;
|
| 675 | use crate::writer::Indentation;
|
| 676 | use pretty_assertions::assert_eq;
|
| 677 |
|
| 678 | /// Checks that given `$data` successfully serialized as `$expected`
|
| 679 | macro_rules! serialize_as {
|
| 680 | ($name:ident: $data:expr => $expected:literal) => {
|
| 681 | #[test]
|
| 682 | fn $name() {
|
| 683 | let mut buffer = String::new();
|
| 684 | let ser = ContentSerializer {
|
| 685 | writer: &mut buffer,
|
| 686 | level: QuoteLevel::Full,
|
| 687 | indent: Indent::Owned(Indentation::new(b' ' , 2)),
|
| 688 | write_indent: false,
|
| 689 | expand_empty_elements: false,
|
| 690 | };
|
| 691 |
|
| 692 | $data.serialize(ser).unwrap();
|
| 693 | assert_eq!(buffer, $expected);
|
| 694 | }
|
| 695 | };
|
| 696 | }
|
| 697 |
|
| 698 | /// Checks that attempt to serialize given `$data` results to a
|
| 699 | /// serialization error `$kind` with `$reason`
|
| 700 | macro_rules! err {
|
| 701 | ($name:ident: $data:expr => $kind:ident($reason:literal)) => {
|
| 702 | #[test]
|
| 703 | fn $name() {
|
| 704 | let mut buffer = String::new();
|
| 705 | let ser = ContentSerializer {
|
| 706 | writer: &mut buffer,
|
| 707 | level: QuoteLevel::Full,
|
| 708 | indent: Indent::Owned(Indentation::new(b' ' , 2)),
|
| 709 | write_indent: false,
|
| 710 | expand_empty_elements: false,
|
| 711 | };
|
| 712 |
|
| 713 | match $data.serialize(ser).unwrap_err() {
|
| 714 | DeError::$kind(e) => assert_eq!(e, $reason),
|
| 715 | e => panic!(
|
| 716 | "Expected `{}({})`, found `{:?}`" ,
|
| 717 | stringify!($kind),
|
| 718 | $reason,
|
| 719 | e
|
| 720 | ),
|
| 721 | }
|
| 722 | // We can write something before fail
|
| 723 | // assert_eq!(buffer, "");
|
| 724 | }
|
| 725 | };
|
| 726 | }
|
| 727 |
|
| 728 | serialize_as!(false_: false => "false" );
|
| 729 | serialize_as!(true_: true => "true" );
|
| 730 |
|
| 731 | serialize_as!(i8_: -42i8 => "-42" );
|
| 732 | serialize_as!(i16_: -4200i16 => "-4200" );
|
| 733 | serialize_as!(i32_: -42000000i32 => "-42000000" );
|
| 734 | serialize_as!(i64_: -42000000000000i64 => "-42000000000000" );
|
| 735 | serialize_as!(isize_: -42000000000000isize => "-42000000000000" );
|
| 736 |
|
| 737 | serialize_as!(u8_: 42u8 => "42" );
|
| 738 | serialize_as!(u16_: 4200u16 => "4200" );
|
| 739 | serialize_as!(u32_: 42000000u32 => "42000000" );
|
| 740 | serialize_as!(u64_: 42000000000000u64 => "42000000000000" );
|
| 741 | serialize_as!(usize_: 42000000000000usize => "42000000000000" );
|
| 742 |
|
| 743 | serde_if_integer128! {
|
| 744 | serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000" );
|
| 745 | serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000" );
|
| 746 | }
|
| 747 |
|
| 748 | serialize_as!(f32_: 4.2f32 => "4.2" );
|
| 749 | serialize_as!(f64_: 4.2f64 => "4.2" );
|
| 750 |
|
| 751 | serialize_as!(char_non_escaped: 'h' => "h" );
|
| 752 | serialize_as!(char_lt: '<' => "<" );
|
| 753 | serialize_as!(char_gt: '>' => ">" );
|
| 754 | serialize_as!(char_amp: '&' => "&" );
|
| 755 | serialize_as!(char_apos: ' \'' => "'" );
|
| 756 | serialize_as!(char_quot: '"' => """ );
|
| 757 | //TODO: add a setting to escape leading/trailing spaces, in order to
|
| 758 | // pretty-print does not change the content
|
| 759 | serialize_as!(char_space: ' ' => " " );
|
| 760 |
|
| 761 | serialize_as!(str_non_escaped: "non-escaped string" => "non-escaped string" );
|
| 762 | serialize_as!(str_escaped: "< \"escaped & string'>" => "<"escaped & string'>" );
|
| 763 |
|
| 764 | err!(bytes: Bytes(b"< \"escaped & bytes'>" ) => Unsupported("`serialize_bytes` not supported yet" ));
|
| 765 |
|
| 766 | serialize_as!(option_none: Option::<Enum>::None => "" );
|
| 767 | serialize_as!(option_some: Some(Enum::Unit) => "<Unit/>" );
|
| 768 |
|
| 769 | serialize_as!(unit: () => "" );
|
| 770 | serialize_as!(unit_struct: Unit => "" );
|
| 771 | serialize_as!(unit_struct_escaped: UnitEscaped => "" );
|
| 772 |
|
| 773 | // Unlike SimpleTypeSerializer, enumeration values serialized as tags
|
| 774 | serialize_as!(enum_unit: Enum::Unit => "<Unit/>" );
|
| 775 | err!(enum_unit_escaped: Enum::UnitEscaped
|
| 776 | => Unsupported("character `<` is not allowed at the start of an XML name `< \"&'>`" ));
|
| 777 |
|
| 778 | // Newtypes recursively applies ContentSerializer
|
| 779 | serialize_as!(newtype: Newtype(42) => "42" );
|
| 780 | serialize_as!(enum_newtype: Enum::Newtype(42) => "<Newtype>42</Newtype>" );
|
| 781 |
|
| 782 | // Note that sequences of primitives serialized without delimiters other that indent!
|
| 783 | serialize_as!(seq: vec![1, 2, 3]
|
| 784 | => "1 \n\
|
| 785 | 2 \n\
|
| 786 | 3" );
|
| 787 | serialize_as!(seq_empty: Vec::<usize>::new() => "" );
|
| 788 | serialize_as!(tuple: ("< \"&'>" , "with \t\r\n spaces" , 3usize)
|
| 789 | => "<"&'> \n\
|
| 790 | with \t\r\n spaces \n\
|
| 791 | 3" );
|
| 792 | serialize_as!(tuple_struct: Tuple("first" , 42)
|
| 793 | => "first \n\
|
| 794 | 42" );
|
| 795 | serialize_as!(enum_tuple: Enum::Tuple("first" , 42)
|
| 796 | => "<Tuple>first</Tuple> \n\
|
| 797 | <Tuple>42</Tuple>" );
|
| 798 |
|
| 799 | // Structured types cannot be serialized without surrounding tag, which
|
| 800 | // only `enum` can provide
|
| 801 | err!(map: BTreeMap::from([("_1" , 2), ("_3" , 4)])
|
| 802 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
| 803 | err!(struct_: Struct { key: "answer" , val: (42, 42) }
|
| 804 | => Unsupported("serialization of struct `Struct` is not supported in `$value` field" ));
|
| 805 | serialize_as!(enum_struct: Enum::Struct { key: "answer" , val: (42, 42) }
|
| 806 | => "<Struct> \n \
|
| 807 | <key>answer</key> \n \
|
| 808 | <val>42</val> \n \
|
| 809 | <val>42</val> \n\
|
| 810 | </Struct>" );
|
| 811 |
|
| 812 | /// Special field name `$text` should be serialized as text content
|
| 813 | mod text {
|
| 814 | use super::*;
|
| 815 | use pretty_assertions::assert_eq;
|
| 816 |
|
| 817 | err!(map: BTreeMap::from([("$text" , 2), ("_3" , 4)])
|
| 818 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
| 819 | err!(struct_:
|
| 820 | Text {
|
| 821 | before: "answer" ,
|
| 822 | content: (42, 42),
|
| 823 | after: "answer" ,
|
| 824 | }
|
| 825 | => Unsupported("serialization of struct `Text` is not supported in `$value` field" ));
|
| 826 | serialize_as!(enum_struct:
|
| 827 | SpecialEnum::Text {
|
| 828 | before: "answer" ,
|
| 829 | content: (42, 42),
|
| 830 | after: "answer" ,
|
| 831 | }
|
| 832 | => "<Text> \n \
|
| 833 | <before>answer</before> \n \
|
| 834 | 42 42 \n \
|
| 835 | <after>answer</after> \n\
|
| 836 | </Text>" );
|
| 837 | }
|
| 838 |
|
| 839 | mod attributes {
|
| 840 | use super::*;
|
| 841 | use pretty_assertions::assert_eq;
|
| 842 |
|
| 843 | err!(map_attr: BTreeMap::from([("@key1" , 1), ("@key2" , 2)])
|
| 844 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
| 845 | err!(map_mixed: BTreeMap::from([("@key1" , 1), ("key2" , 2)])
|
| 846 | => Unsupported("serialization of map types is not supported in `$value` field" ));
|
| 847 |
|
| 848 | err!(struct_: Attributes { key: "answer" , val: (42, 42) }
|
| 849 | => Unsupported("serialization of struct `Attributes` is not supported in `$value` field" ));
|
| 850 | err!(struct_before: AttributesBefore { key: "answer" , val: 42 }
|
| 851 | => Unsupported("serialization of struct `AttributesBefore` is not supported in `$value` field" ));
|
| 852 | err!(struct_after: AttributesAfter { key: "answer" , val: 42 }
|
| 853 | => Unsupported("serialization of struct `AttributesAfter` is not supported in `$value` field" ));
|
| 854 |
|
| 855 | serialize_as!(enum_: Enum::Attributes { key: "answer" , val: (42, 42) }
|
| 856 | => r#"<Attributes key="answer" val="42 42"/>"# );
|
| 857 | serialize_as!(enum_before: Enum::AttributesBefore { key: "answer" , val: 42 }
|
| 858 | => "<AttributesBefore key= \"answer \"> \n \
|
| 859 | <val>42</val> \n\
|
| 860 | </AttributesBefore>" );
|
| 861 | serialize_as!(enum_after: Enum::AttributesAfter { key: "answer" , val: 42 }
|
| 862 | => "<AttributesAfter val= \"42 \"> \n \
|
| 863 | <key>answer</key> \n\
|
| 864 | </AttributesAfter>" );
|
| 865 | }
|
| 866 | }
|
| 867 | }
|
| 868 | |