aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml5
-rw-r--r--src/api_interface_definition.rs398
-rw-r--r--src/command.rs629
-rw-r--r--src/deserialization/buffer_deserializer.rs240
-rw-r--r--src/deserialization/mod.rs127
-rw-r--r--src/lib.rs62
-rw-r--r--src/test_utils.rs44
-rw-r--r--src/util.rs15
8 files changed, 583 insertions, 937 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 3af2b0f..d2ea91f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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)),
- )
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 4e33f8c..8a992b8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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;