diff options
| author | HampusM <hampus@hampusmat.com> | 2023-05-14 19:51:17 +0200 | 
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2023-05-14 19:51:17 +0200 | 
| commit | ad142e9e749adf64642168c0d221b0cf47f149c7 (patch) | |
| tree | 642308f9aec6032115c62bb6ae269fa37c3fc453 | |
| parent | 4d46e44801d8ef40c8a2f4dea1d2c9088f2557a1 (diff) | |
| -rw-r--r-- | Cargo.toml | 5 | ||||
| -rw-r--r-- | src/api_interface_definition.rs | 398 | ||||
| -rw-r--r-- | src/command.rs | 629 | ||||
| -rw-r--r-- | src/deserialization/buffer_deserializer.rs | 240 | ||||
| -rw-r--r-- | src/deserialization/mod.rs | 127 | ||||
| -rw-r--r-- | src/lib.rs | 62 | ||||
| -rw-r--r-- | src/test_utils.rs | 44 | ||||
| -rw-r--r-- | src/util.rs | 15 | 
8 files changed, 583 insertions, 937 deletions
@@ -24,13 +24,14 @@ all-features = true  rustdoc-args = ["--cfg", "doc_cfg"]  [dependencies] -quick-xml = { version = "0.27.1" }  thiserror = "1.0.38" +xml-stinks = { version = "0.1.0" }  [dev-dependencies]  pretty_assertions = "1.3.0"  criterion = "0.4.0" -ridicule = "0.3.0" +mockall = "0.11.4" +xml-stinks = { version = "0.1.0", features = ["deserializer-static-generics"] }  [[bench]]  name = "registry" diff --git a/src/api_interface_definition.rs b/src/api_interface_definition.rs index 784b7d0..8208dbe 100644 --- a/src/api_interface_definition.rs +++ b/src/api_interface_definition.rs @@ -1,8 +1,12 @@  //! GL API interface definition. -use quick_xml::events::BytesStart; +use std::borrow::Cow; -use crate::deserialization::{Deserialize, Deserializer, DeserializerError}; +use xml_stinks::deserializer::Deserializer; +use xml_stinks::tagged::{TagStart, TagStartError}; +use xml_stinks::{impl_from_deserializer_error, DeserializeTagged}; + +use crate::util::impl_from_deserializer_err_wrapped;  use crate::DeserializationError;  /// GL API interface definition. @@ -73,48 +77,39 @@ impl APIInterfaceDefinition      }  } -impl Deserialize for APIInterfaceDefinition +impl DeserializeTagged for APIInterfaceDefinition  {      type Error = Error;      fn deserialize<TDeserializer: Deserializer>( -        start: &BytesStart, +        start: &TagStart,          deserializer: &mut TDeserializer,      ) -> Result<Self, Self::Error>      { -        let api_name = String::from_utf8( -            start -                .try_get_attribute(b"api") -                .map_err(DeserializerError::ReadFailed)? -                .ok_or_else(|| Error::MissingAPIName)? -                .value -                .into_owned(), -        ) -        .map_err(|_| Error::APINameNotUTF8)?; - -        let name = String::from_utf8( -            start -                .try_get_attribute(b"name") -                .map_err(DeserializerError::ReadFailed)? -                .ok_or_else(|| Error::MissingName)? -                .value -                .into_owned(), -        ) -        .map_err(|_| Error::NameNotUTF8)?; - -        let version = String::from_utf8( -            start -                .try_get_attribute(b"number") -                .map_err(DeserializerError::ReadFailed)? -                .ok_or_else(|| Error::MissingVersionNumber)? -                .value -                .into_owned(), -        ) -        .map_err(|_| Error::VersionNotUTF8)?; - -        let requirements = deserializer.de_tag_list::<Requirement>("require")?; - -        let removals = deserializer.de_tag_list::<Removal>("remove")?; +        let api_name = start +            .get_attribute("api")? +            .ok_or_else(|| Error::MissingAPIName)? +            .value() +            .map_err(|_| Error::APINameNotUTF8)? +            .into_owned(); + +        let name = start +            .get_attribute("name")? +            .ok_or_else(|| Error::MissingName)? +            .value() +            .map_err(|_| Error::NameNotUTF8)? +            .into_owned(); + +        let version = start +            .get_attribute("number")? +            .ok_or_else(|| Error::MissingVersionNumber)? +            .value() +            .map_err(|_| Error::VersionNotUTF8)? +            .into_owned(); + +        let requirements = deserializer.de_tag_list::<Requirement, _>(Some("require"))?; + +        let removals = deserializer.de_tag_list::<Removal, _>(Some("remove"))?;          Ok(Self {              api_name, @@ -154,19 +149,26 @@ pub enum Error      #[error("Version is not valid UTF-8")]      VersionNotUTF8, +    /// Invalid requirement. +    #[error("Invalid requirement")] +    InvalidRequirement(#[from] RequirementError), + +    /// Invalid removal. +    #[error("Invalid removal")] +    InvalidRemoval(#[from] RemovalError), +      /// Deserialization failed.      #[error("Deserialization failed")]      DeserializationFailed(#[from] DeserializationError), -} -impl From<DeserializerError> for Error -{ -    fn from(err: DeserializerError) -> Self -    { -        DeserializationError(err).into() -    } +    /// Invalid tag start. +    #[error("Invalid tag start")] +    InvalidTagStart(#[from] TagStartError),  } +impl_from_deserializer_err_wrapped!(Error); +impl_from_deserializer_error!(Error); +  /// GL API feature requirement.  #[derive(Debug, PartialEq, Eq)]  pub struct Requirement @@ -215,38 +217,36 @@ impl Requirement      }  } -impl Deserialize for Requirement +impl DeserializeTagged for Requirement  {      type Error = RequirementError;      fn deserialize<TDeserializer: Deserializer>( -        start: &BytesStart, +        start: &TagStart,          deserializer: &mut TDeserializer,      ) -> Result<Self, Self::Error>      { -        let profile = match start -            .try_get_attribute(b"profile") -            .map_err(DeserializerError::ReadFailed)? -        { +        let profile = match start.get_attribute("profile")? {              Some(comment_attr) => Some( -                String::from_utf8(comment_attr.value.into_owned()) -                    .map_err(|_| RequirementError::ProfileNotUTF8)?, +                comment_attr +                    .value() +                    .map_err(|_| RequirementError::ProfileNotUTF8)? +                    .into_owned(),              ),              None => None,          }; -        let comment = match start -            .try_get_attribute(b"comment") -            .map_err(DeserializerError::ReadFailed)? -        { +        let comment = match start.get_attribute("comment")? {              Some(comment_attr) => Some( -                String::from_utf8(comment_attr.value.into_owned()) -                    .map_err(|_| RequirementError::CommentNotUTF8)?, +                comment_attr +                    .value() +                    .map_err(|_| RequirementError::CommentNotUTF8)? +                    .into_owned(),              ),              None => None,          }; -        let features = deserializer.de_list::<Feature>()?; +        let features = deserializer.de_tag_list::<Feature, _>(None::<&str>)?;          Ok(Self {              profile, @@ -268,19 +268,22 @@ pub enum RequirementError      #[error("Comment is not valid UTF-8")]      CommentNotUTF8, +    /// Invalid feature. +    #[error("Invalid feature")] +    InvalidFeature(#[from] FeatureError), +      /// Deserialization failed.      #[error("Deserialization failed")]      DeserializationFailed(#[from] DeserializationError), -} -impl From<DeserializerError> for RequirementError -{ -    fn from(err: DeserializerError) -> Self -    { -        DeserializationError(err).into() -    } +    /// Invalid tag start. +    #[error("Invalid tag start")] +    InvalidTagStart(#[from] TagStartError),  } +impl_from_deserializer_err_wrapped!(RequirementError); +impl_from_deserializer_error!(RequirementError); +  /// GL API feature removal.  #[derive(Debug, PartialEq, Eq)]  pub struct Removal @@ -329,36 +332,30 @@ impl Removal      }  } -impl Deserialize for Removal +impl DeserializeTagged for Removal  {      type Error = RemovalError;      fn deserialize<TDeserializer: Deserializer>( -        start: &BytesStart, +        start: &TagStart,          deserializer: &mut TDeserializer,      ) -> Result<Self, Self::Error>      { -        let profile = String::from_utf8( -            start -                .try_get_attribute(b"profile") -                .map_err(DeserializerError::ReadFailed)? -                .ok_or_else(|| RemovalError::MissingProfile)? -                .value -                .into_owned(), -        ) -        .map_err(|_| RemovalError::ProfileNotUTF8)?; - -        let comment = String::from_utf8( -            start -                .try_get_attribute(b"comment") -                .map_err(DeserializerError::ReadFailed)? -                .ok_or_else(|| RemovalError::MissingComment)? -                .value -                .into_owned(), -        ) -        .map_err(|_| RemovalError::CommentNotUTF8)?; - -        let features = deserializer.de_list::<Feature>()?; +        let profile = start +            .get_attribute("profile")? +            .ok_or_else(|| RemovalError::MissingProfile)? +            .value() +            .map_err(|_| RemovalError::ProfileNotUTF8)? +            .into_owned(); + +        let comment = start +            .get_attribute("comment")? +            .ok_or_else(|| RemovalError::MissingComment)? +            .value() +            .map_err(|_| RemovalError::CommentNotUTF8)? +            .into_owned(); + +        let features = deserializer.de_tag_list::<Feature, _>(None::<&str>)?;          Ok(Self {              profile, @@ -388,19 +385,22 @@ pub enum RemovalError      #[error("Comment is not valid UTF-8")]      CommentNotUTF8, +    /// Invalid feature. +    #[error("Invalid feature")] +    InvalidFeature(#[from] FeatureError), +      /// Deserialization failed.      #[error("Deserialization failed")]      DeserializationFailed(#[from] DeserializationError), -} -impl From<DeserializerError> for RemovalError -{ -    fn from(err: DeserializerError) -> Self -    { -        DeserializationError(err).into() -    } +    /// Invalid tag start. +    #[error("Invalid tag start")] +    InvalidTagStart(#[from] TagStartError),  } +impl_from_deserializer_err_wrapped!(RemovalError); +impl_from_deserializer_error!(RemovalError); +  /// GL API feature.  #[derive(Debug, PartialEq, Eq)]  pub struct Feature @@ -449,16 +449,16 @@ impl Feature      }  } -impl Deserialize for Feature +impl DeserializeTagged for Feature  {      type Error = FeatureError;      fn deserialize<TDeserializer: Deserializer>( -        start: &BytesStart, +        start: &TagStart,          _deserializer: &mut TDeserializer,      ) -> Result<Self, Self::Error>      { -        let kind = match start.name().as_ref() { +        let kind = match start.name_bytes() {              b"enum" => Ok(FeatureKind::Enum),              b"command" => Ok(FeatureKind::Command),              b"type" => Ok(FeatureKind::Type), @@ -467,26 +467,22 @@ impl Deserialize for Feature              )),          }?; -        let name = String::from_utf8( -            start -                .try_get_attribute(b"name") -                .map_err(DeserializerError::ReadFailed)? -                .ok_or_else(|| FeatureError::MissingName)? -                .value -                .into_owned(), -        ) -        .map_err(|_| FeatureError::NameNotUTF8)?; - -        let comment = match start -            .try_get_attribute(b"comment") -            .map_err(DeserializerError::ReadFailed)? -        { -            Some(comment_attr) => Some( -                String::from_utf8(comment_attr.value.into_owned()) -                    .map_err(|_| FeatureError::CommentNotUTF8)?, -            ), -            None => None, -        }; +        let name = start +            .get_attribute("name")? +            .ok_or_else(|| FeatureError::MissingName)? +            .value() +            .map_err(|_| FeatureError::NameNotUTF8)? +            .into_owned(); + +        let comment = start +            .get_attribute("comment")? +            .map(|comment_attr| { +                comment_attr +                    .value() +                    .map(Cow::into_owned) +                    .map_err(|_| FeatureError::CommentNotUTF8) +            }) +            .transpose()?;          Ok(Self {              kind, @@ -519,16 +515,15 @@ pub enum FeatureError      /// Deserialization failed.      #[error("Deserialization failed")]      DeserializationFailed(#[from] DeserializationError), -} -impl From<DeserializerError> for FeatureError -{ -    fn from(err: DeserializerError) -> Self -    { -        DeserializationError(err).into() -    } +    /// Invalid tag start. +    #[error("Invalid tag start")] +    InvalidTagStart(#[from] TagStartError),  } +impl_from_deserializer_err_wrapped!(FeatureError); +impl_from_deserializer_error!(FeatureError); +  /// GL API feature kind.  #[derive(Debug, Clone, Copy, PartialEq, Eq)]  pub enum FeatureKind @@ -547,9 +542,10 @@ pub enum FeatureKind  mod tests  {      use pretty_assertions::{assert_eq, assert_str_eq}; +    use xml_stinks::attribute::Attribute;      use super::*; -    use crate::deserialization::MockDeserializer; +    use crate::test_utils::MockDeserializer;      #[test]      fn deserialize_feature_works() @@ -557,7 +553,8 @@ mod tests          let mut mock_deserializer = MockDeserializer::new();          let commentless_enum_feature = Feature::deserialize( -            &BytesStart::new("enum").with_attributes([("name", "GL_BLEND")]), +            &TagStart::new("enum") +                .with_attributes([Attribute::new("name", "GL_BLEND".as_bytes())]),              &mut mock_deserializer,          )          .expect("Expected Ok"); @@ -567,9 +564,12 @@ mod tests          assert!(commentless_enum_feature.comment.is_none());          let enum_feature = Feature::deserialize( -            &BytesStart::new("enum").with_attributes([ -                ("name", "GL_VERTEX_BINDING_BUFFER"), -                ("comment", "Added in 2013/10/22 update to the spec"), +            &TagStart::new("enum").with_attributes([ +                Attribute::new("name", "GL_VERTEX_BINDING_BUFFER".as_bytes()), +                Attribute::new( +                    "comment", +                    "Added in 2013/10/22 update to the spec".as_bytes(), +                ),              ]),              &mut mock_deserializer,          ) @@ -584,7 +584,8 @@ mod tests          );          let command_feature = Feature::deserialize( -            &BytesStart::new("command").with_attributes([("name", "glCreateBuffers")]), +            &TagStart::new("command") +                .with_attributes([Attribute::new("name", "glCreateBuffers".as_bytes())]),              &mut mock_deserializer,          )          .expect("Expected Ok"); @@ -594,7 +595,8 @@ mod tests          assert!(command_feature.comment.is_none());          let type_feature = Feature::deserialize( -            &BytesStart::new("type").with_attributes([("name", "GLbyte")]), +            &TagStart::new("type") +                .with_attributes([Attribute::new("name", "GLbyte".as_bytes())]),              &mut mock_deserializer,          )          .expect("Expected Ok"); @@ -604,7 +606,7 @@ mod tests          assert!(type_feature.comment.is_none());          assert!(matches!( -            Feature::deserialize(&BytesStart::new("foo"), &mut mock_deserializer), +            Feature::deserialize(&TagStart::new("foo"), &mut mock_deserializer),              Err(FeatureError::UnknownFeatureKind(_))          ));      } @@ -614,22 +616,22 @@ mod tests      {          let mut mock_deserializer = MockDeserializer::new(); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer.expect_de_list().times(1).returning(|_| { +        mock_deserializer +            .expect_de_tag_list::<_, &str>() +            .times(1) +            .returning(|_| {                  Ok(vec![                      Feature::new(FeatureKind::Command, "glNewList", None),                      Feature::new(FeatureKind::Command, "glEndList", None),                  ])              }); -        }          let removal = Removal::deserialize( -            &BytesStart::new("remove").with_attributes([ -                ("profile", "core"), -                ( +            &TagStart::new("remove").with_attributes([ +                Attribute::new("profile", "core".as_bytes()), +                Attribute::new(                      "comment", -                    "Compatibility-only GL 1.0 features removed from GL 3.2", +                    "Compatibility-only GL 1.0 features removed from GL 3.2".as_bytes(),                  ),              ]),              &mut mock_deserializer, @@ -651,39 +653,39 @@ mod tests      {          let mut mock_deserializer = MockDeserializer::new(); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer.expect_de_list().times(1).returning(|_| { +        mock_deserializer +            .expect_de_tag_list::<_, &str>() +            .times(1) +            .returning(|_| {                  Ok(vec![                      Feature::new(FeatureKind::Command, "glNewList", None),                      Feature::new(FeatureKind::Command, "glEndList", None),                  ])              }); -        } -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer.expect_de_list().times(1).returning(|_| { +        mock_deserializer +            .expect_de_tag_list::<_, &str>() +            .times(1) +            .returning(|_| {                  Ok(vec![                      Feature::new(FeatureKind::Enum, "GL_COLOR_TABLE", None),                      Feature::new(FeatureKind::Enum, "GL_MINMAX", None),                      Feature::new(FeatureKind::Enum, "GL_HISTOGRAM", None),                  ])              }); -        } -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer.expect_de_list().times(1).returning(|_| { +        mock_deserializer +            .expect_de_tag_list::<_, &str>() +            .times(1) +            .returning(|_| {                  Ok(vec![                      Feature::new(FeatureKind::Enum, "GL_STACK_UNDERFLOW", None),                      Feature::new(FeatureKind::Enum, "GL_STACK_OVERFLOW", None),                  ])              }); -        }          let requirement = -            Requirement::deserialize(&BytesStart::new("require"), &mut mock_deserializer) +            Requirement::deserialize(&TagStart::new("require"), &mut mock_deserializer)                  .expect("Expected Ok");          assert!(requirement.profile.is_none()); @@ -692,7 +694,8 @@ mod tests          assert_eq!(requirement.features.len(), 2);          let requirement = Requirement::deserialize( -            &BytesStart::new("require").with_attributes([("profile", "compatibility")]), +            &TagStart::new("require") +                .with_attributes([Attribute::new("profile", "compatibility".as_bytes())]),              &mut mock_deserializer,          )          .expect("Expected Ok"); @@ -703,14 +706,15 @@ mod tests          assert_eq!(requirement.features.len(), 3);          let requirement = Requirement::deserialize( -            &BytesStart::new("require").with_attributes([ -                ("profile", "core"), -                ( +            &TagStart::new("require").with_attributes([ +                Attribute::new("profile", "core".as_bytes()), +                Attribute::new(                      "comment",                      concat!(                          "Restore functionality removed in GL 3.2 core ",                          "to GL 4.3. Needed for debug interface." -                    ), +                    ) +                    .as_bytes(),                  ),              ]),              &mut mock_deserializer, @@ -734,49 +738,43 @@ mod tests      {          let mut mock_deserializer = MockDeserializer::new(); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_tag_list() -                .times(1) -                .returning(|_, _| { -                    Ok(vec![ -                        Requirement::new( -                            None, -                            None, -                            vec![ -                                Feature::new(FeatureKind::Command, "glFenceSync", None), -                                Feature::new(FeatureKind::Enum, "GL_WAIT_FAILED", None), -                            ], -                        ), -                        Requirement::new( -                            None, -                            Some("Reuse ARB_sync".to_string()), -                            [Feature::new(FeatureKind::Enum, "GL_OBJECT_TYPE", None)], -                        ), -                    ]) -                }); -        } +        mock_deserializer +            .expect_de_tag_list::<_, &str>() +            .times(1) +            .returning(|_| { +                Ok(vec![ +                    Requirement::new( +                        None, +                        None, +                        vec![ +                            Feature::new(FeatureKind::Command, "glFenceSync", None), +                            Feature::new(FeatureKind::Enum, "GL_WAIT_FAILED", None), +                        ], +                    ), +                    Requirement::new( +                        None, +                        Some("Reuse ARB_sync".to_string()), +                        [Feature::new(FeatureKind::Enum, "GL_OBJECT_TYPE", None)], +                    ), +                ]) +            }); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_tag_list() -                .times(1) -                .returning(|_, _| { -                    Ok(vec![Removal::new( -                        "core", -                        "Compatibility-only GL 1.0 features removed from GL 3.2", -                        vec![Feature::new(FeatureKind::Command, "glBegin", None)], -                    )]) -                }); -        } +        mock_deserializer +            .expect_de_tag_list::<_, &str>() +            .times(1) +            .returning(|_| { +                Ok(vec![Removal::new( +                    "core", +                    "Compatibility-only GL 1.0 features removed from GL 3.2", +                    vec![Feature::new(FeatureKind::Command, "glBegin", None)], +                )]) +            });          let api_interface_definition = APIInterfaceDefinition::deserialize( -            &BytesStart::new("feature").with_attributes([ -                ("api", "gl"), -                ("name", "GL_VERSION_3_2"), -                ("number", "3.2"), +            &TagStart::new("feature").with_attributes([ +                Attribute::new("api", "gl".as_bytes()), +                Attribute::new("name", "GL_VERSION_3_2".as_bytes()), +                Attribute::new("number", "3.2".as_bytes()),              ]),              &mut mock_deserializer,          ) diff --git a/src/command.rs b/src/command.rs index deab0eb..a0c9e23 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,14 +1,11 @@  //! OpenGL command. -use quick_xml::events::BytesStart; - -use crate::deserialization::{ -    Deserialize, -    DeserializeWithFn, -    Deserializer, -    DeserializerError, -    IgnoreEnd, -    ResultExt, -}; +use std::convert::Infallible; + +use xml_stinks::deserializer::{Deserializer, Error as DeserializerError, IgnoreEnd}; +use xml_stinks::tagged::{TagStart, TagStartError}; +use xml_stinks::{impl_from_deserializer_error, DeserializeTagged, ResultExt}; + +use crate::util::impl_from_deserializer_err_wrapped;  use crate::DeserializationError;  /// A command. @@ -48,21 +45,20 @@ impl Command      }  } -impl Deserialize for Command +impl DeserializeTagged for Command  {      type Error = Error;      fn deserialize<TDeserializer: Deserializer>( -        start: &BytesStart, +        start: &TagStart,          deserializer: &mut TDeserializer,      ) -> Result<Self, Self::Error>      {          let prototype = deserializer.de_tag::<Prototype>("proto", IgnoreEnd::No)?; -        let parameters = deserializer.de_tag_list::<Parameter>("param")?; +        let parameters = deserializer.de_tag_list::<Parameter, _>(Some("param"))?; -        deserializer -            .skip_to_tag_end(std::str::from_utf8(start.name().as_ref()).unwrap())?; +        deserializer.skip_to_tag_end(start.name()?)?;          Ok(Self {              prototype, @@ -90,16 +86,15 @@ pub enum Error      /// Deserialization failed.      #[error("Deserialization failed")]      DeserializationFailed(#[from] DeserializationError), -} -impl From<DeserializerError> for Error -{ -    fn from(err: DeserializerError) -> Self -    { -        DeserializationError(err).into() -    } +    /// Invalid tag start. +    #[error("Invalid tag start")] +    InvalidTagStart(#[from] TagStartError),  } +impl_from_deserializer_err_wrapped!(Error); +impl_from_deserializer_error!(Error); +  /// A command prototype.  #[derive(Debug, Clone, PartialEq, Eq)]  pub struct Prototype @@ -134,22 +129,22 @@ impl Prototype      }  } -impl Deserialize for Prototype +impl DeserializeTagged for Prototype  {      type Error = PrototypeError;      fn deserialize<TDeserializer: Deserializer>( -        _start: &BytesStart, +        _start: &TagStart,          deserializer: &mut TDeserializer,      ) -> Result<Self, Self::Error>      {          let return_type = deserialize_type::<PrototypeError>(deserializer)?; -        let name = deserializer.de_tag_with::<_, _, DeserializeWithFn<_, _, _>>( -            "name", -            IgnoreEnd::No, -            |_, deserializer| deserializer.de_text(), -        )?; +        // let name = deserializer.de_tag_with::<_, _, DeserializeWithFn<_, _, _>>( +        let name = +            deserializer.de_tag_with("name", IgnoreEnd::No, |_, deserializer| { +                deserializer.de_text() +            })?;          Ok(Self { name, return_type })      } @@ -172,13 +167,8 @@ pub enum PrototypeError      DeserializationFailed(#[from] DeserializationError),  } -impl From<DeserializerError> for PrototypeError -{ -    fn from(err: DeserializerError) -> Self -    { -        DeserializationError(err).into() -    } -} +impl_from_deserializer_err_wrapped!(PrototypeError); +impl_from_deserializer_error!(PrototypeError);  /// A command parameter.  #[derive(Debug, Clone, PartialEq, Eq)] @@ -214,22 +204,22 @@ impl Parameter      }  } -impl Deserialize for Parameter +impl DeserializeTagged for Parameter  {      type Error = ParameterError;      fn deserialize<TDeserializer: Deserializer>( -        _start: &BytesStart, +        _start: &TagStart,          deserializer: &mut TDeserializer,      ) -> Result<Self, Self::Error>      {          let ty = deserialize_type::<ParameterError>(deserializer)?; -        let name = deserializer.de_tag_with::<_, _, DeserializeWithFn<_, _, _>>( -            "name", -            IgnoreEnd::No, -            |_, deserializer| deserializer.de_text(), -        )?; +        // let name = deserializer.de_tag_with::<_, _, DeserializeWithFn<_, _, _>>( +        let name = +            deserializer.de_tag_with("name", IgnoreEnd::No, |_, deserializer| { +                deserializer.de_text() +            })?;          Ok(Self { name, ty })      } @@ -248,27 +238,22 @@ pub enum ParameterError      DeserializationFailed(#[from] DeserializationError),  } -impl From<DeserializerError> for ParameterError -{ -    fn from(err: DeserializerError) -> Self -    { -        DeserializationError(err).into() -    } -} +impl_from_deserializer_err_wrapped!(ParameterError); +impl_from_deserializer_error!(ParameterError);  fn deserialize_type<Err>(deserializer: &mut impl Deserializer) -> Result<String, Err>  where -    Err: From<DeserializerError>, +    Err: From<DeserializerError<Infallible>>,  {      let type_before = deserializer.de_text().try_event()?; +    //.de_tag_with::<_, _, DeserializeWithFn<_, _, _>>(      let type_ptype = deserializer -        .de_tag_with::<_, _, DeserializeWithFn<_, _, _>>( -            "ptype", -            IgnoreEnd::No, -            |_, deserializer| deserializer.de_text(), -        ) -        .try_event()?; +        .de_tag_with("ptype", IgnoreEnd::No, |_, deserializer| { +            deserializer.de_text() +        }) +        .try_event() +        .map_err(Into::into)?;      let type_after = deserializer.de_text().try_event()?; @@ -308,92 +293,83 @@ where  #[cfg(test)]  mod tests  { +    use mockall::predicate::{always, eq, function};      use pretty_assertions::assert_str_eq; -    use quick_xml::events::Event; -    use ridicule::predicate::{always, eq, function};      use super::*; -    use crate::deserialization::MockDeserializer; +    use crate::test_utils::MockDeserializer;      #[test]      fn deserialize_prototype_works_with_ptype()      {          let mut mock_deserializer = MockDeserializer::new(); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| { -                    Err(DeserializerError::UnexpectedEvent { -                        expected_event_name: "text".to_string(), -                        found_event: Event::Start(BytesStart::new("ptype")), -                    }) +        mock_deserializer +            .expect_de_text() +            .returning(|| { +                Err(DeserializerError::UnexpectedEvent { +                    expected_event_name: "text".to_string(), +                    found_event: "ptype".to_string(),                  }) -                .times(1); -        } - -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| Ok("GLuint".to_string())) -                .times(1); -        } +            }) +            .times(1); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -            .expect_de_tag_with::<String, DeserializerError, DeserializeWithFn<_, _, _>>() +        mock_deserializer +            .expect_de_tag_with::<String, DeserializerError<Infallible>>()              .with(                  eq("ptype"),                  function(|ignore_end| matches!(ignore_end, IgnoreEnd::No)),                  always(),              ) -            .returning(|deserializer, tag_name, _, func| { -                func(&BytesStart::new(tag_name), deserializer) +            .returning(|tag_name, _, func| { +                let mut inner_mock_deserializer = MockDeserializer::new(); + +                inner_mock_deserializer +                    .expect_de_text() +                    .returning(|| Ok("GLuint".to_string())) +                    .times(1); + +                Ok(func( +                    &TagStart::new(tag_name), +                    &mut inner_mock_deserializer, +                )?)              })              .times(1); -        } -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| { -                    Err(DeserializerError::UnexpectedEvent { -                        expected_event_name: "text".to_string(), -                        found_event: Event::Start(BytesStart::new("name")), -                    }) +        mock_deserializer +            .expect_de_text() +            .returning(|| { +                Err(DeserializerError::UnexpectedEvent { +                    expected_event_name: "text".to_string(), +                    found_event: "name".to_string(),                  }) -                .times(1); -        } - -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| Ok("glDoComplicatedThing".to_string())) -                .times(1); -        } +            }) +            .times(1); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_tag_with::<String, DeserializerError, DeserializeWithFn<_, _, _>>() -                .with( -                    eq("name"), -                    function(|ignore_end| matches!(ignore_end, IgnoreEnd::No)), -                    always(), -                ) -                .returning(|deserializer, tag_name, _, func| { -                    func(&BytesStart::new(tag_name), deserializer) -                }) -                .times(1); -        } +        mock_deserializer +            .expect_de_tag_with::<String, DeserializerError<Infallible>>() +            .with( +                eq("name"), +                function(|ignore_end| matches!(ignore_end, IgnoreEnd::No)), +                always(), +            ) +            .returning(|tag_name, _, func| { +                let mut inner_mock_deserializer = MockDeserializer::new(); + +                inner_mock_deserializer +                    .expect_de_text() +                    .returning(|| Ok("glDoComplicatedThing".to_string())) +                    .times(1); + +                Ok(func( +                    &TagStart::new(tag_name), +                    &mut inner_mock_deserializer, +                )?) +            }) +            .times(1);          let prototype = -            Prototype::deserialize(&BytesStart::new("proto"), &mut mock_deserializer) +            Prototype::deserialize(&TagStart::new("proto"), &mut mock_deserializer)                  .expect("Expected Ok");          assert_str_eq!(prototype.name, "glDoComplicatedThing"); @@ -405,70 +381,60 @@ mod tests      {          let mut mock_deserializer = MockDeserializer::new(); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| Ok("void".to_string())) -                .times(1); -        } +        mock_deserializer +            .expect_de_text() +            .returning(|| Ok("void".to_string())) +            .times(1); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -            .expect_de_tag_with::<String, DeserializerError, DeserializeWithFn<_, _, _>>() +        mock_deserializer +            .expect_de_tag_with::<String, DeserializerError<Infallible>>()              .with(                  eq("ptype"),                  function(|ignore_end| matches!(ignore_end, IgnoreEnd::No)),                  always(),              ) -            .returning(|_, _, _, _| { +            .returning(|_, _, _| {                  Err(DeserializerError::UnexpectedEvent {                      expected_event_name: "start".to_string(), -                    found_event: Event::Start(BytesStart::new("name")), +                    found_event: "name".to_string(),                  })              })              .times(1); -        } -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| { -                    Err(DeserializerError::UnexpectedEvent { -                        expected_event_name: "text".to_string(), -                        found_event: Event::Start(BytesStart::new("name")), -                    }) +        mock_deserializer +            .expect_de_text() +            .returning(|| { +                Err(DeserializerError::UnexpectedEvent { +                    expected_event_name: "text".to_string(), +                    found_event: "name".to_string(),                  }) -                .times(1); -        } - -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| Ok("glDoSomeThing".to_string())) -                .times(1); -        } +            }) +            .times(1); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -            .expect_de_tag_with::<String, DeserializerError, DeserializeWithFn<_, _, _>>() +        mock_deserializer +            .expect_de_tag_with::<String, DeserializerError<Infallible>>()              .with(                  eq("name"),                  function(|ignore_end| matches!(ignore_end, IgnoreEnd::No)),                  always(),              ) -            .returning(|deserializer, tag_name, _, func| { -                func(&BytesStart::new(tag_name), deserializer) +            .returning(|tag_name, _, func| { +                let mut inner_mock_deserializer = MockDeserializer::new(); + +                inner_mock_deserializer +                    .expect_de_text() +                    .returning(|| Ok("glDoSomeThing".to_string())) +                    .times(1); + +                Ok(func( +                    &TagStart::new(tag_name), +                    &mut inner_mock_deserializer, +                )?)              })              .times(1); -        }          let prototype = -            Prototype::deserialize(&BytesStart::new("proto"), &mut mock_deserializer) +            Prototype::deserialize(&TagStart::new("proto"), &mut mock_deserializer)                  .expect("Expected Ok");          assert_str_eq!(prototype.name, "glDoSomeThing"); @@ -480,80 +446,72 @@ mod tests      {          let mut mock_deserializer = MockDeserializer::new(); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| { -                    Err(DeserializerError::UnexpectedEvent { -                        expected_event_name: "text".to_string(), -                        found_event: Event::Start(BytesStart::new("ptype")), -                    }) +        mock_deserializer +            .expect_de_text() +            .returning(|| { +                Err(DeserializerError::UnexpectedEvent { +                    expected_event_name: "text".to_string(), +                    found_event: "ptype".to_string(),                  }) -                .times(1); -        } - -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| Ok("GLenum".to_string())) -                .times(1); -        } +            }) +            .times(1); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -            .expect_de_tag_with::<String, DeserializerError, DeserializeWithFn<_, _, _>>() +        mock_deserializer +            .expect_de_tag_with::<String, DeserializerError<Infallible>>()              .with(                  eq("ptype"),                  function(|ignore_end| matches!(ignore_end, IgnoreEnd::No)),                  always(),              ) -            .returning(|deserializer, tag_name, _, func| { -                func(&BytesStart::new(tag_name), deserializer) +            .returning(|tag_name, _, func| { +                let mut inner_mock_deserializer = MockDeserializer::new(); + +                inner_mock_deserializer +                    .expect_de_text() +                    .returning(|| Ok("GLenum".to_string())) +                    .times(1); + +                Ok(func( +                    &TagStart::new(tag_name), +                    &mut inner_mock_deserializer, +                )?)              })              .times(1); -        } -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| { -                    Err(DeserializerError::UnexpectedEvent { -                        expected_event_name: "text".to_string(), -                        found_event: Event::Start(BytesStart::new("name")), -                    }) +        mock_deserializer +            .expect_de_text() +            .returning(|| { +                Err(DeserializerError::UnexpectedEvent { +                    expected_event_name: "text".to_string(), +                    found_event: "name".to_string(),                  }) -                .times(1); -        } - -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| Ok("value".to_string())) -                .times(1); -        } +            }) +            .times(1); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -            .expect_de_tag_with::<String, DeserializerError, DeserializeWithFn<_, _, _>>() +        mock_deserializer +            .expect_de_tag_with::<String, DeserializerError<Infallible>>()              .with(                  eq("name"),                  function(|ignore_end| matches!(ignore_end, IgnoreEnd::No)),                  always(),              ) -            .returning(|deserializer, tag_name, _, func| { -                func(&BytesStart::new(tag_name), deserializer) +            .returning(|tag_name, _, func| { +                let mut inner_mock_deserializer = MockDeserializer::new(); + +                inner_mock_deserializer +                    .expect_de_text() +                    .returning(|| Ok("value".to_string())) +                    .times(1); + +                Ok(func( +                    &TagStart::new(tag_name), +                    &mut inner_mock_deserializer, +                )?)              })              .times(1); -        }          let parameter = -            Parameter::deserialize(&BytesStart::new("param"), &mut mock_deserializer) +            Parameter::deserialize(&TagStart::new("param"), &mut mock_deserializer)                  .expect("Expected Ok");          assert_str_eq!(parameter.name, "value"); @@ -565,74 +523,67 @@ mod tests      {          let mut mock_deserializer = MockDeserializer::new(); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| { -                    Err(DeserializerError::UnexpectedEvent { -                        expected_event_name: "text".to_string(), -                        found_event: Event::Start(BytesStart::new("ptype")), -                    }) +        mock_deserializer +            .expect_de_text() +            .returning(|| { +                Err(DeserializerError::UnexpectedEvent { +                    expected_event_name: "text".to_string(), +                    found_event: "ptype".to_string(),                  }) -                .times(1); -        } - -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| Ok("GLchar".to_string())) -                .times(1); -        } +            }) +            .times(1); -        unsafe { -            mock_deserializer -            .expect_de_tag_with::<String, DeserializerError, DeserializeWithFn<_, _, _>>() +        mock_deserializer +            .expect_de_tag_with::<String, DeserializerError<Infallible>>()              .with(                  eq("ptype"),                  function(|ignore_end| matches!(ignore_end, IgnoreEnd::No)),                  always(),              ) -            .returning(|deserializer, tag_name, _, func| { -                func(&BytesStart::new(tag_name), deserializer) +            .returning(|tag_name, _, func| { +                let mut inner_mock_deserializer = MockDeserializer::new(); + +                inner_mock_deserializer +                    .expect_de_text() +                    .returning(|| Ok("GLchar".to_string())) +                    .times(1); + +                Ok(func( +                    &TagStart::new(tag_name), +                    &mut inner_mock_deserializer, +                )?)              })              .times(1); -        } - -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| Ok("*".to_string())) -                .times(1); -        } -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| Ok("source".to_string())) -                .times(1); -        } +        mock_deserializer +            .expect_de_text() +            .returning(|| Ok("*".to_string())) +            .times(1); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -            .expect_de_tag_with::<String, DeserializerError, DeserializeWithFn<_, _, _>>() +        mock_deserializer +            .expect_de_tag_with::<String, DeserializerError<Infallible>>()              .with(                  eq("name"),                  function(|ignore_end| matches!(ignore_end, IgnoreEnd::No)),                  always(),              ) -            .returning(|deserializer, tag_name, _, func| { -                func(&BytesStart::new(tag_name), deserializer) +            .returning(|tag_name, _, func| { +                let mut inner_mock_deserializer = MockDeserializer::new(); + +                inner_mock_deserializer +                    .expect_de_text() +                    .returning(|| Ok("source".to_string())) +                    .times(1); + +                Ok(func( +                    &TagStart::new(tag_name), +                    &mut inner_mock_deserializer, +                )?)              })              .times(1); -        }          let parameter = -            Parameter::deserialize(&BytesStart::new("param"), &mut mock_deserializer) +            Parameter::deserialize(&TagStart::new("param"), &mut mock_deserializer)                  .expect("Expected Ok");          assert_str_eq!(parameter.name, "source"); @@ -644,70 +595,62 @@ mod tests      {          let mut mock_deserializer = MockDeserializer::new(); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| Ok("const".to_string())) -                .times(1); -        } - -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| Ok("GLchar".to_string())) -                .times(1); -        } +        mock_deserializer +            .expect_de_text() +            .returning(|| Ok("const".to_string())) +            .times(1); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -            .expect_de_tag_with::<String, DeserializerError, DeserializeWithFn<_, _, _>>() +        mock_deserializer +            .expect_de_tag_with::<String, DeserializerError<Infallible>>()              .with(                  eq("ptype"),                  function(|ignore_end| matches!(ignore_end, IgnoreEnd::No)),                  always(),              ) -            .returning(|deserializer, tag_name, _, func| { -                func(&BytesStart::new(tag_name), deserializer) +            .returning(|tag_name, _, func| { +                let mut inner_mock_deserializer = MockDeserializer::new(); + +                inner_mock_deserializer +                    .expect_de_text() +                    .returning(|| Ok("GLchar".to_string())) +                    .times(1); + +                Ok(func( +                    &TagStart::new(tag_name), +                    &mut inner_mock_deserializer, +                )?)              })              .times(1); -        } - -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| Ok("*".to_string())) -                .times(1); -        } -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| Ok("name".to_string())) -                .times(1); -        } +        mock_deserializer +            .expect_de_text() +            .returning(|| Ok("*".to_string())) +            .times(1); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -            .expect_de_tag_with::<String, DeserializerError, DeserializeWithFn<_, _, _>>() +        mock_deserializer +            .expect_de_tag_with::<String, DeserializerError<Infallible>>()              .with(                  eq("name"),                  function(|ignore_end| matches!(ignore_end, IgnoreEnd::No)),                  always(),              ) -            .returning(|deserializer, tag_name, _, func| { -                func(&BytesStart::new(tag_name), deserializer) +            .returning(|tag_name, _, func| { +                let mut inner_mock_deserializer = MockDeserializer::new(); + +                inner_mock_deserializer +                    .expect_de_text() +                    .returning(|| Ok("name".to_string())) +                    .times(1); + +                Ok(func( +                    &TagStart::new(tag_name), +                    &mut inner_mock_deserializer, +                )?)              })              .times(1); -        }          let parameter = -            Parameter::deserialize(&BytesStart::new("param"), &mut mock_deserializer) +            Parameter::deserialize(&TagStart::new("param"), &mut mock_deserializer)                  .expect("Expected Ok");          assert_str_eq!(parameter.name, "name"); @@ -719,70 +662,60 @@ mod tests      {          let mut mock_deserializer = MockDeserializer::new(); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| Ok("void *".to_string())) -                .times(1); -        } +        mock_deserializer +            .expect_de_text() +            .returning(|| Ok("void *".to_string())) +            .times(1); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -            .expect_de_tag_with::<String, DeserializerError, DeserializeWithFn<_, _, _>>() +        mock_deserializer +            .expect_de_tag_with::<String, DeserializerError<Infallible>>()              .with(                  eq("ptype"),                  function(|ignore_end| matches!(ignore_end, IgnoreEnd::No)),                  always(),              ) -            .returning(|_, _, _, _| { +            .returning(|_, _, _| {                  Err(DeserializerError::UnexpectedEvent {                      expected_event_name: "start".to_string(), -                    found_event: Event::Start(BytesStart::new("name")), +                    found_event: "name".to_string(),                  })              })              .times(1); -        } -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| { -                    Err(DeserializerError::UnexpectedEvent { -                        expected_event_name: "text".to_string(), -                        found_event: Event::Start(BytesStart::new("name")), -                    }) +        mock_deserializer +            .expect_de_text() +            .returning(|| { +                Err(DeserializerError::UnexpectedEvent { +                    expected_event_name: "text".to_string(), +                    found_event: "start".to_string(),                  }) -                .times(1); -        } - -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -                .expect_de_text() -                .returning(|_| Ok("pixels".to_string())) -                .times(1); -        } +            }) +            .times(1); -        // SAFETY: No argument lifetime is mistreated -        unsafe { -            mock_deserializer -            .expect_de_tag_with::<String, DeserializerError, DeserializeWithFn<_, _, _>>() +        mock_deserializer +            .expect_de_tag_with::<String, DeserializerError<Infallible>>()              .with(                  eq("name"),                  function(|ignore_end| matches!(ignore_end, IgnoreEnd::No)),                  always(),              ) -            .returning(|deserializer, tag_name, _, func| { -                func(&BytesStart::new(tag_name), deserializer) +            .returning(|tag_name, _, func| { +                let mut inner_mock_deserializer = MockDeserializer::new(); + +                inner_mock_deserializer +                    .expect_de_text() +                    .returning(|| Ok("pixels".to_string())) +                    .times(1); + +                Ok(func( +                    &TagStart::new(tag_name), +                    &mut inner_mock_deserializer, +                )?)              })              .times(1); -        }          let parameter = -            Parameter::deserialize(&BytesStart::new("param"), &mut mock_deserializer) +            Parameter::deserialize(&TagStart::new("param"), &mut mock_deserializer)                  .expect("Expected Ok");          assert_str_eq!(parameter.name, "pixels"); diff --git a/src/deserialization/buffer_deserializer.rs b/src/deserialization/buffer_deserializer.rs deleted file mode 100644 index a4e7b2f..0000000 --- a/src/deserialization/buffer_deserializer.rs +++ /dev/null @@ -1,240 +0,0 @@ -use std::any::type_name; -use std::error::Error; -use std::io::BufRead; - -use quick_xml::events::{BytesStart, Event}; -use quick_xml::Reader; - -use crate::deserialization::{ -    Deserialize, -    Deserializer, -    DeserializerError, -    IgnoreEnd, -    WrappedDeserializeError, -}; - -macro_rules! read_event { -    ($self: ident) => {{ -        let event = if let Some(leftover_event) = $self.leftover_event.take() { -            leftover_event -        } else { -            $self.reader.read_event_into(&mut $self.buf)?.into_owned() -        }; - -        if let Event::Eof = &event { -            return Err(DeserializerError::UnexpectedEndOfFile); -        } - -        event -    }}; -} - -pub struct BufferDeserializer<Source> -{ -    reader: Reader<Source>, -    leftover_event: Option<Event<'static>>, -    buf: Vec<u8>, -} - -impl<Source> BufferDeserializer<Source> -where -    Source: BufRead, -{ -    pub fn new(source: Source) -> Self -    { -        let mut reader = Reader::from_reader(source); - -        reader.trim_text(true); -        reader.expand_empty_elements(true); - -        Self { -            reader, -            leftover_event: None, -            buf: Vec::new(), -        } -    } -} - -impl<Source> Deserializer for BufferDeserializer<Source> -where -    Source: BufRead, -{ -    fn de_tag<De: Deserialize>( -        &mut self, -        tag_name: &str, -        ignore_end: IgnoreEnd, -    ) -> Result<De, DeserializerError> -    { -        self.de_tag_with(tag_name, ignore_end, De::deserialize) -    } - -    fn de_tag_with<Output, Err, DeserializeFn>( -        &mut self, -        tag_name: &str, -        ignore_end: IgnoreEnd, -        deserialize: DeserializeFn, -    ) -> Result<Output, DeserializerError> -    where -        Err: Error + Send + Sync + 'static, -        DeserializeFn: FnOnce(&BytesStart, &mut Self) -> Result<Output, Err>, -    { -        let deserialized = match read_event!(self) { -            Event::Start(start) if start.name().as_ref() == tag_name.as_bytes() => { -                deserialize(&start, self).map_err(|err| { -                    DeserializerError::DeserializeFailed( -                        type_name::<Output>(), -                        WrappedDeserializeError::new(err), -                    ) -                })? -            } -            event => { -                self.leftover_event = Some(event.clone().into_owned()); - -                return Err(DeserializerError::UnexpectedEvent { -                    expected_event_name: format!("start({tag_name})"), -                    found_event: event, -                }); -            } -        }; - -        if let IgnoreEnd::No = ignore_end { -            self.read_end_event(tag_name.as_bytes())?; -        } - -        Ok(deserialized) -    } - -    fn de_tag_list<De: Deserialize>( -        &mut self, -        tag_name: &str, -    ) -> Result<Vec<De>, DeserializerError> -    { -        let mut deserialized_items = Vec::new(); - -        loop { -            let start = match read_event!(self) { -                Event::Start(start) if start.name().as_ref() == tag_name.as_bytes() => { -                    start -                } -                Event::Comment(_) => { -                    continue; -                } -                event => { -                    self.leftover_event = Some(event.into_owned()); -                    break; -                } -            }; - -            let deserialized = De::deserialize(&start, self).map_err(|err| { -                DeserializerError::DeserializeFailed( -                    type_name::<De>(), -                    WrappedDeserializeError::new(err), -                ) -            })?; - -            self.read_end_event(tag_name.as_bytes())?; - -            deserialized_items.push(deserialized); -        } - -        Ok(deserialized_items) -    } - -    fn de_list<De: Deserialize>(&mut self) -> Result<Vec<De>, DeserializerError> -    { -        let mut deserialized_items = Vec::new(); - -        loop { -            let start = match read_event!(self) { -                Event::Start(start) => start, -                Event::Comment(_) => { -                    continue; -                } -                event => { -                    self.leftover_event = Some(event.into_owned()); -                    break; -                } -            }; - -            let deserialized = De::deserialize(&start, self).map_err(|err| { -                DeserializerError::DeserializeFailed( -                    type_name::<De>(), -                    WrappedDeserializeError::new(err), -                ) -            })?; - -            self.read_end_event(start.name().as_ref())?; - -            deserialized_items.push(deserialized); -        } - -        Ok(deserialized_items) -    } - -    fn de_text(&mut self) -> Result<String, DeserializerError> -    { -        let text = match read_event!(self) { -            Event::Text(text) => Ok(text), -            event => { -                self.leftover_event = Some(event.clone().into_owned()); - -                Err(DeserializerError::UnexpectedEvent { -                    expected_event_name: "text".to_string(), -                    found_event: event, -                }) -            } -        }? -        .unescape()?; - -        Ok(text.to_string()) -    } - -    fn skip_to_tag_start(&mut self, tag_name: &str) -> Result<(), DeserializerError> -    { -        loop { -            match read_event!(self) { -                Event::Start(start) if start.name().as_ref() == tag_name.as_bytes() => { -                    self.leftover_event = Some(Event::Start(start).into_owned()); - -                    break; -                } -                _ => {} -            } -        } - -        Ok(()) -    } - -    fn skip_to_tag_end(&mut self, tag_name: &str) -> Result<(), DeserializerError> -    { -        loop { -            match read_event!(self) { -                Event::End(end) if end.name().as_ref() == tag_name.as_bytes() => { -                    self.leftover_event = Some(Event::End(end).into_owned()); - -                    return Ok(()); -                } -                _ => {} -            } -        } -    } -} - -impl<Source> BufferDeserializer<Source> -where -    Source: BufRead, -{ -    fn read_end_event(&mut self, tag_name: &[u8]) -> Result<(), DeserializerError> -    { -        let event = read_event!(self); - -        if matches!(&event, Event::End(end) if end.name().as_ref() == tag_name) { -            return Ok(()); -        } - -        Err(DeserializerError::UnexpectedEvent { -            expected_event_name: "end".to_string(), -            found_event: event.into_owned(), -        }) -    } -} diff --git a/src/deserialization/mod.rs b/src/deserialization/mod.rs deleted file mode 100644 index b86c2af..0000000 --- a/src/deserialization/mod.rs +++ /dev/null @@ -1,127 +0,0 @@ -use std::error::Error; -use std::ops::Deref; - -use quick_xml::events::{BytesStart, Event}; - -pub mod buffer_deserializer; - -pub trait Deserialize: Sized -{ -    type Error: Error + Send + Sync + 'static; - -    fn deserialize<TDeserializer: Deserializer>( -        start: &BytesStart, -        deserializer: &mut TDeserializer, -    ) -> Result<Self, Self::Error>; -} - -#[cfg_attr(test, ridicule::automock)] -pub trait Deserializer -{ -    fn de_tag<De: Deserialize>( -        &mut self, -        tag_name: &str, -        ignore_end: IgnoreEnd, -    ) -> Result<De, DeserializerError>; - -    fn de_tag_with<Output, Err, DeserializeFn>( -        &mut self, -        tag_name: &str, -        ignore_end: IgnoreEnd, -        deserialize: DeserializeFn, -    ) -> Result<Output, DeserializerError> -    where -        Err: Error + Send + Sync + 'static, -        DeserializeFn: FnOnce(&BytesStart, &mut Self) -> Result<Output, Err>; - -    fn de_tag_list<De: Deserialize>( -        &mut self, -        tag_name: &str, -    ) -> Result<Vec<De>, DeserializerError>; - -    fn de_list<De: Deserialize>(&mut self) -> Result<Vec<De>, DeserializerError>; - -    fn de_text(&mut self) -> Result<String, DeserializerError>; - -    fn skip_to_tag_start(&mut self, tag_name: &str) -> Result<(), DeserializerError>; - -    fn skip_to_tag_end(&mut self, tag_name: &str) -> Result<(), DeserializerError>; -} - -pub enum IgnoreEnd -{ -    Yes, -    No, -} - -/// Function pointer type passable to [`Deserializer::de_tag_with`]. -pub type DeserializeWithFn<Output, Err, Deserializer> = -    fn(&BytesStart, &mut Deserializer) -> Result<Output, Err>; - -#[derive(Debug, thiserror::Error)] -pub enum DeserializerError -{ -    #[error("Failed to read")] -    ReadFailed(#[from] quick_xml::Error), - -    #[error("Failed to deserialize {0}")] -    DeserializeFailed(&'static str, #[source] WrappedDeserializeError), - -    #[error("Expected {expected_event_name} event. Found {found_event:?}")] -    UnexpectedEvent -    { -        expected_event_name: String, -        found_event: Event<'static>, -    }, - -    #[error("Unexpected end of file")] -    UnexpectedEndOfFile, -} - -#[derive(Debug, thiserror::Error)] -#[error(transparent)] -pub struct WrappedDeserializeError(Box<dyn Error + Send + Sync>); - -impl WrappedDeserializeError -{ -    fn new<Err: Error + Send + Sync + 'static>(err: Err) -> Self -    { -        Self(Box::new(err)) -    } -} - -impl Deref for WrappedDeserializeError -{ -    type Target = dyn Error; - -    fn deref(&self) -> &Self::Target -    { -        self.0.as_ref() -    } -} - -pub trait ResultExt<Value> -{ -    fn try_event(self) -> Result<Option<Value>, DeserializerError>; -} - -impl<Value> ResultExt<Value> for Result<Value, DeserializerError> -{ -    fn try_event(self) -> Result<Option<Value>, DeserializerError> -    { -        self.map_or_else( -            |err| { -                if let DeserializerError::UnexpectedEvent { -                    expected_event_name: _, -                    found_event: _, -                } = err -                { -                    return Ok(None); -                } - -                Err(err) -            }, -            |value| Ok(Some(value)), -        ) -    } -} @@ -21,20 +21,33 @@  #![cfg_attr(doc_cfg, feature(doc_cfg))]  #![deny(clippy::all, clippy::pedantic, missing_docs)] +use std::convert::Infallible;  use std::fs::File;  use std::io::Read; -use quick_xml::events::BytesStart; - -use crate::api_interface_definition::APIInterfaceDefinition; +use xml_stinks::deserializer::buffered::Buffered as BufferedDeserializer; +use xml_stinks::deserializer::{Deserializer, Error as DeserializerError, IgnoreEnd}; +use xml_stinks::tagged::TagStart; +use xml_stinks::{ +    impl_from_deserializer_error, +    xml_stinks_if_deserializer_static_generics, +    DeserializeTagged, +}; + +use crate::api_interface_definition::{ +    APIInterfaceDefinition, +    Error as APIInterfaceDefinitionError, +};  use crate::command::{Command, Error as CommandError}; -use crate::deserialization::buffer_deserializer::BufferDeserializer; -use crate::deserialization::{Deserialize, Deserializer, DeserializerError, IgnoreEnd}; +use crate::util::impl_from_deserializer_err_wrapped;  pub mod api_interface_definition;  pub mod command; -mod deserialization; +mod util; + +#[cfg(test)] +mod test_utils;  /// XML.  #[cfg(feature = "include-xml")] @@ -69,7 +82,17 @@ impl Registry      /// Returns `Err` if parsing fails in any way.      pub fn retrieve_from_bytes(xml_bytes: &[u8]) -> Result<Registry, RegistryError>      { -        let mut deserializer = BufferDeserializer::new(xml_bytes); +        let mut deserializer = xml_stinks_if_deserializer_static_generics!(then { +            // The deserializer-static-generics feature of the dependency xml-stinks is +            // enabled. +            // +            // The feature is enabled when building tests and is required for mocking to +            // work. However, it will reject any non-owned source, so the bytes has to be +            // made into a Cursor<Vec<u8>> +            BufferedDeserializer::new(std::io::Cursor::new(xml_bytes.to_vec())); +        } else { +            BufferedDeserializer::new(xml_bytes); +        });          deserializer.skip_to_tag_start(REGISTRY_TAG_NAME)?; @@ -125,12 +148,12 @@ impl Registry      }  } -impl Deserialize for Registry +impl DeserializeTagged for Registry  {      type Error = RegistryError;      fn deserialize<TDeserializer: Deserializer>( -        _start: &BytesStart, +        _start: &TagStart,          deserializer: &mut TDeserializer,      ) -> Result<Self, Self::Error>      { @@ -138,13 +161,13 @@ impl Deserialize for Registry          let commands =              deserializer.de_tag_with("commands", IgnoreEnd::No, |_, deserializer| { -                deserializer.de_tag_list::<Command>("command") +                deserializer.de_tag_list::<Command, _>(Some("command"))              })?;          deserializer.skip_to_tag_start("feature")?;          let api_interface_definitions = -            deserializer.de_tag_list::<APIInterfaceDefinition>("feature")?; +            deserializer.de_tag_list::<APIInterfaceDefinition, _>(Some("feature"))?;          Ok(Self {              commands, @@ -165,10 +188,14 @@ pub enum RegistryError      #[error("No 'commands' element was found")]      MissingCommandsElement, -    /// A command is invalid. +    /// Invalid command.      #[error("Invalid command")]      InvalidCommand(#[from] CommandError), +    /// Invalid API interface definition. +    #[error("Invalid API interface definition")] +    InvalidAPIInterfaceDefinition(#[from] APIInterfaceDefinitionError), +      /// I/O failed.      #[error("I/O failed")]      IOFailed(#[from] std::io::Error), @@ -178,18 +205,13 @@ pub enum RegistryError      DeserializationFailed(#[from] DeserializationError),  } -impl From<DeserializerError> for RegistryError -{ -    fn from(err: DeserializerError) -> Self -    { -        DeserializationError(err).into() -    } -} +impl_from_deserializer_err_wrapped!(RegistryError); +impl_from_deserializer_error!(RegistryError);  /// Deserialization error.  #[derive(Debug, thiserror::Error)]  #[error(transparent)] -pub struct DeserializationError(#[from] DeserializerError); +pub struct DeserializationError(#[from] DeserializerError<Infallible>);  #[cfg(test)]  mod tests diff --git a/src/test_utils.rs b/src/test_utils.rs new file mode 100644 index 0000000..0f80cb7 --- /dev/null +++ b/src/test_utils.rs @@ -0,0 +1,44 @@ +use std::convert::Infallible; + +use mockall::mock; +use xml_stinks::deserializer::{Deserializer, Error, IgnoreEnd, MaybeStatic}; +use xml_stinks::tagged::TagStart; +use xml_stinks::DeserializeTagged; + +mock! { +    pub Deserializer {} + +    impl Deserializer for Deserializer +    { +        fn de_tag<De: DeserializeTagged>( +            &mut self, +            tag_name: &str, +            ignore_end: IgnoreEnd, +        ) -> Result<De, Error<De::Error>>; + +        fn de_tag_with<TOutput, Err, Func>( +            &mut self, +            tag_name: &str, +            ignore_end: IgnoreEnd, +            deserialize: Func, +        ) -> Result<TOutput, Error<Err>> +        where +            TOutput: MaybeStatic, +            Err: std::error::Error + Send + Sync + 'static, +            Func: FnOnce(&TagStart, &mut MockDeserializer) -> Result<TOutput, Err> + MaybeStatic; + +        fn de_tag_list<De, TagName>( +            &mut self, +            tag_name: Option<TagName> +        ) -> Result<Vec<De>, Error<De::Error>> +            where +                De: DeserializeTagged, +                TagName: AsRef<str> + MaybeStatic; + +        fn de_text(&mut self) -> Result<String, Error<Infallible>>; + +        fn skip_to_tag_start(&mut self, tag_name: &str) -> Result<(), Error<Infallible>>; + +        fn skip_to_tag_end(&mut self, tag_name: &str) -> Result<(), Error<Infallible>>; +    } +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..fd1f767 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,15 @@ +macro_rules! impl_from_deserializer_err_wrapped { +    ($err: ident) => { +        impl From<::xml_stinks::deserializer::Error<::std::convert::Infallible>> for $err +        { +            fn from( +                err: ::xml_stinks::deserializer::Error<::std::convert::Infallible>, +            ) -> Self +            { +                Self::DeserializationFailed(err.into()) +            } +        } +    }; +} + +pub(crate) use impl_from_deserializer_err_wrapped;  | 
