From ad142e9e749adf64642168c0d221b0cf47f149c7 Mon Sep 17 00:00:00 2001 From: HampusM Date: Sun, 14 May 2023 19:51:17 +0200 Subject: refactor: use xml-stinks --- src/api_interface_definition.rs | 398 ++++++++++++++++++++-------------------- 1 file changed, 198 insertions(+), 200 deletions(-) (limited to 'src/api_interface_definition.rs') 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( - start: &BytesStart, + start: &TagStart, deserializer: &mut TDeserializer, ) -> Result { - 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::("require")?; - - let removals = deserializer.de_tag_list::("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::(Some("require"))?; + + let removals = deserializer.de_tag_list::(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 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( - start: &BytesStart, + start: &TagStart, deserializer: &mut TDeserializer, ) -> Result { - 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::()?; + let features = deserializer.de_tag_list::(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 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( - start: &BytesStart, + start: &TagStart, deserializer: &mut TDeserializer, ) -> Result { - 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::()?; + 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::(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 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( - start: &BytesStart, + start: &TagStart, _deserializer: &mut TDeserializer, ) -> Result { - 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 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, ) -- cgit v1.2.3-18-g5258