use crate::se::SeError; use serde::ser::{Impossible, Serialize, Serializer}; use serde::serde_if_integer128; use std::fmt::Write; /// A serializer, that ensures, that only plain types can be serialized, /// so result can be used as an XML tag or attribute name. /// /// This serializer does not check that name does not contain characters that /// [not allowed] in XML names, because in some cases it should pass names /// that would be filtered on higher level. /// /// [not allowed]: https://www.w3.org/TR/xml11/#sec-common-syn pub struct QNameSerializer { /// Writer to which this serializer writes content pub writer: W, } impl QNameSerializer { #[inline] fn write_str(&mut self, value: &str) -> Result<(), SeError> { Ok(self.writer.write_str(value)?) } } impl Serializer for QNameSerializer { type Ok = W; type Error = SeError; type SerializeSeq = Impossible; type SerializeTuple = Impossible; type SerializeTupleStruct = Impossible; type SerializeTupleVariant = Impossible; type SerializeMap = Impossible; type SerializeStruct = Impossible; type SerializeStructVariant = Impossible; write_primitive!(); fn serialize_str(mut self, value: &str) -> Result { self.write_str(value)?; Ok(self.writer) } /// Because unit type can be represented only by empty string which is not /// a valid XML name, serialization of unit returns `Err(Unsupported)` fn serialize_unit(self) -> Result { Err(SeError::Unsupported( "cannot serialize unit type `()` as an XML tag name".into(), )) } /// Because unit struct can be represented only by empty string which is not /// a valid XML name, serialization of unit struct returns `Err(Unsupported)` fn serialize_unit_struct(self, name: &'static str) -> Result { Err(SeError::Unsupported( format!("cannot serialize unit struct `{}` as an XML tag name", name).into(), )) } /// We cannot store both a variant discriminant and a variant value, /// so serialization of enum newtype variant returns `Err(Unsupported)` fn serialize_newtype_variant( self, name: &'static str, _variant_index: u32, variant: &'static str, _value: &T, ) -> Result { Err(SeError::Unsupported( format!( "cannot serialize enum newtype variant `{}::{}` as an XML tag name", name, variant ) .into(), )) } fn serialize_seq(self, _len: Option) -> Result { Err(SeError::Unsupported( "cannot serialize sequence as an XML tag name".into(), )) } fn serialize_tuple(self, _len: usize) -> Result { Err(SeError::Unsupported( "cannot serialize tuple as an XML tag name".into(), )) } fn serialize_tuple_struct( self, name: &'static str, _len: usize, ) -> Result { Err(SeError::Unsupported( format!( "cannot serialize tuple struct `{}` as an XML tag name", name ) .into(), )) } fn serialize_tuple_variant( self, name: &'static str, _variant_index: u32, variant: &'static str, _len: usize, ) -> Result { Err(SeError::Unsupported( format!( "cannot serialize enum tuple variant `{}::{}` as an XML tag name", name, variant ) .into(), )) } fn serialize_map(self, _len: Option) -> Result { Err(SeError::Unsupported( "cannot serialize map as an XML tag name".into(), )) } fn serialize_struct( self, name: &'static str, _len: usize, ) -> Result { Err(SeError::Unsupported( format!("cannot serialize struct `{}` as an XML tag name", name).into(), )) } fn serialize_struct_variant( self, name: &'static str, _variant_index: u32, variant: &'static str, _len: usize, ) -> Result { Err(SeError::Unsupported( format!( "cannot serialize enum struct variant `{}::{}` as an XML tag name", name, variant ) .into(), )) } } #[cfg(test)] mod tests { use super::*; use crate::utils::Bytes; use pretty_assertions::assert_eq; use serde::Serialize; use std::collections::BTreeMap; #[derive(Debug, Serialize, PartialEq)] struct Unit; #[derive(Debug, Serialize, PartialEq)] struct Newtype(bool); #[derive(Debug, Serialize, PartialEq)] struct Tuple(&'static str, usize); #[derive(Debug, Serialize, PartialEq)] struct Struct { key: &'static str, val: usize, } #[derive(Debug, Serialize, PartialEq)] enum Enum { Unit, #[serde(rename = "<\"&'>")] UnitEscaped, Newtype(bool), Tuple(&'static str, usize), Struct { key: &'static str, val: usize, }, } /// Checks that given `$data` successfully serialized as `$expected` macro_rules! serialize_as { ($name:ident: $data:expr => $expected:literal) => { #[test] fn $name() { let ser = QNameSerializer { writer: String::new(), }; let buffer = $data.serialize(ser).unwrap(); assert_eq!(buffer, $expected); } }; } /// Checks that attempt to serialize given `$data` results to a /// serialization error `$kind` with `$reason` macro_rules! err { ($name:ident: $data:expr => $kind:ident($reason:literal)) => { #[test] fn $name() { let mut buffer = String::new(); let ser = QNameSerializer { writer: &mut buffer, }; match $data.serialize(ser).unwrap_err() { SeError::$kind(e) => assert_eq!(e, $reason), e => panic!( "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, e ), } assert_eq!(buffer, ""); } }; } serialize_as!(false_: false => "false"); serialize_as!(true_: true => "true"); serialize_as!(i8_: -42i8 => "-42"); serialize_as!(i16_: -4200i16 => "-4200"); serialize_as!(i32_: -42000000i32 => "-42000000"); serialize_as!(i64_: -42000000000000i64 => "-42000000000000"); serialize_as!(isize_: -42000000000000isize => "-42000000000000"); serialize_as!(u8_: 42u8 => "42"); serialize_as!(u16_: 4200u16 => "4200"); serialize_as!(u32_: 42000000u32 => "42000000"); serialize_as!(u64_: 42000000000000u64 => "42000000000000"); serialize_as!(usize_: 42000000000000usize => "42000000000000"); serde_if_integer128! { serialize_as!(i128_: -420000000000000000000000000000i128 => "-420000000000000000000000000000"); serialize_as!(u128_: 420000000000000000000000000000u128 => "420000000000000000000000000000"); } serialize_as!(f32_: 4.2f32 => "4.2"); serialize_as!(f64_: 4.2f64 => "4.2"); serialize_as!(char_non_escaped: 'h' => "h"); serialize_as!(char_lt: '<' => "<"); serialize_as!(char_gt: '>' => ">"); serialize_as!(char_amp: '&' => "&"); serialize_as!(char_apos: '\'' => "'"); serialize_as!(char_quot: '"' => "\""); serialize_as!(str_valid_name: "valid-name" => "valid-name"); serialize_as!(str_space: "string with spaces" => "string with spaces"); serialize_as!(str_lt: "string<" => "string<"); serialize_as!(str_gt: "string>" => "string>"); serialize_as!(str_amp: "string&" => "string&"); serialize_as!(str_apos: "string'" => "string'"); serialize_as!(str_quot: "string\"" => "string\""); err!(bytes: Bytes(b"<\"escaped & bytes'>") => Unsupported("`serialize_bytes` not supported yet")); serialize_as!(option_none: Option::<&str>::None => ""); serialize_as!(option_some: Some("non-escaped-string") => "non-escaped-string"); err!(unit: () => Unsupported("cannot serialize unit type `()` as an XML tag name")); err!(unit_struct: Unit => Unsupported("cannot serialize unit struct `Unit` as an XML tag name")); serialize_as!(enum_unit: Enum::Unit => "Unit"); serialize_as!(enum_unit_escaped: Enum::UnitEscaped => "<\"&'>"); serialize_as!(newtype: Newtype(true) => "true"); err!(enum_newtype: Enum::Newtype(false) => Unsupported("cannot serialize enum newtype variant `Enum::Newtype` as an XML tag name")); err!(seq: vec![1, 2, 3] => Unsupported("cannot serialize sequence as an XML tag name")); err!(tuple: ("<\"&'>", "with\t\r\n spaces", 3usize) => Unsupported("cannot serialize tuple as an XML tag name")); err!(tuple_struct: Tuple("first", 42) => Unsupported("cannot serialize tuple struct `Tuple` as an XML tag name")); err!(enum_tuple: Enum::Tuple("first", 42) => Unsupported("cannot serialize enum tuple variant `Enum::Tuple` as an XML tag name")); err!(map: BTreeMap::from([("_1", 2), ("_3", 4)]) => Unsupported("cannot serialize map as an XML tag name")); err!(struct_: Struct { key: "answer", val: 42 } => Unsupported("cannot serialize struct `Struct` as an XML tag name")); err!(enum_struct: Enum::Struct { key: "answer", val: 42 } => Unsupported("cannot serialize enum struct variant `Enum::Struct` as an XML tag name")); }