diff options
| author | HampusM <hampus@hampusmat.com> | 2023-05-11 21:27:36 +0200 | 
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2023-05-11 21:27:36 +0200 | 
| commit | bdce3a36f72de6097fb78a917e29b7e093a6ef58 (patch) | |
| tree | 7d2cb765c857a3d78cee39c97b926e3b26ec2bd7 | |
| parent | e3a0736f8f73d310a8031fdd169467b7b26bc981 (diff) | |
docs: add examples
| -rw-r--r-- | Cargo.toml | 7 | ||||
| -rw-r--r-- | examples/data.xml | 8 | ||||
| -rw-r--r-- | examples/simple.rs | 143 | ||||
| -rw-r--r-- | examples/testing.rs | 176 | 
4 files changed, 334 insertions, 0 deletions
| @@ -11,3 +11,10 @@ deserializer-static-generics = []  quick-xml = "0.27.1"  thiserror = "1.0.38" +[dev-dependencies] +mockall = "0.11.4" + +[[example]] +name = "testing" +test = true +required-features = ["deserializer-static-generics"] diff --git a/examples/data.xml b/examples/data.xml new file mode 100644 index 0000000..acda261 --- /dev/null +++ b/examples/data.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<data> +	Hello <something>Foo</something> there +	<favorites> +		<food>Icecream</food> +		<color>Magenta</color> +	</favorites> +</data> diff --git a/examples/simple.rs b/examples/simple.rs new file mode 100644 index 0000000..a4b76f2 --- /dev/null +++ b/examples/simple.rs @@ -0,0 +1,143 @@ +use std::convert::Infallible; +use std::error::Error; +use std::fmt::Debug; +use std::fs::File; +use std::io::BufReader; +use std::path::Path; + +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::DeserializeTagged; + +const MANIFEST_DIR: &str = std::env!("CARGO_MANIFEST_DIR"); + +fn main() -> Result<(), Box<dyn Error>> +{ +    let data_file = Path::new(MANIFEST_DIR).join("examples/data.xml"); + +    let reader = BufReader::new(File::open(data_file)?); + +    let mut deserializer = BufferedDeserializer::new(reader); + +    // Skip the <?xml version="1.0" encoding="UTF-8"?> +    deserializer.skip_to_tag_start("data")?; + +    let data = deserializer.de_tag::<Data>("data", IgnoreEnd::No)?; + +    println!("The following data was deserialized:\n"); + +    println!("Greeting: {}", data.greeting); +    println!("Something: {}", data.something); +    println!("Favorite food: {}", data.favorites.food); +    println!("Favorite color: {}", data.favorites.color); + +    Ok(()) +} + +#[derive(Debug)] +struct Data +{ +    greeting: String, +    something: String, +    favorites: Favorites, +} + +impl DeserializeTagged for Data +{ +    type Error = DataError; + +    fn deserialize<TDeserializer: Deserializer>( +        _start: &TagStart, +        deserializer: &mut TDeserializer, +    ) -> Result<Self, Self::Error> +    { +        let greeting_a = deserializer.de_text()?; + +        let something = +            deserializer.de_tag_with("something", IgnoreEnd::No, |_, deserializer| { +                deserializer.de_text() +            })?; + +        let greeting_b = deserializer.de_text()?; + +        let favorites = deserializer.de_tag::<Favorites>("favorites", IgnoreEnd::No)?; + +        Ok(Self { +            greeting: format!("{greeting_a} {greeting_b}"), +            something, +            favorites, +        }) +    } +} + +#[derive(Debug, thiserror::Error)] +enum DataError +{ +    #[error("Invalid favorites")] +    InvalidFavorites(#[from] FavoritesError), + +    #[error(transparent)] +    DeserializeFailed(#[from] DeserializerError<Infallible>), +} + +impl<DeError: Into<Self>> From<DeserializerError<DeError>> for DataError +{ +    fn from(err: DeserializerError<DeError>) -> Self +    { +        if let DeserializerError::DeserializeFailed(de_err) = err { +            return de_err.into(); +        } + +        err.into_never_de_err().into() +    } +} + +#[derive(Debug)] +struct Favorites +{ +    food: String, +    color: String, +} + +impl DeserializeTagged for Favorites +{ +    type Error = FavoritesError; + +    fn deserialize<TDeserializer: Deserializer>( +        _start: &TagStart, +        deserializer: &mut TDeserializer, +    ) -> Result<Self, Self::Error> +    { +        let food = +            deserializer.de_tag_with("food", IgnoreEnd::No, |_, deserializer| { +                deserializer.de_text() +            })?; + +        let color = +            deserializer.de_tag_with("color", IgnoreEnd::No, |_, deserializer| { +                deserializer.de_text() +            })?; + +        Ok(Self { food, color }) +    } +} + +#[derive(Debug, thiserror::Error)] +enum FavoritesError +{ +    #[error(transparent)] +    DeserializeFailed(#[from] DeserializerError<Infallible>), +} + +impl<DeError: Into<Self>> From<DeserializerError<DeError>> for FavoritesError +{ +    fn from(err: DeserializerError<DeError>) -> Self +    { +        if let DeserializerError::DeserializeFailed(de_err) = err { +            return de_err.into(); +        } + +        err.into_never_de_err().into() +    } +} diff --git a/examples/testing.rs b/examples/testing.rs new file mode 100644 index 0000000..3a9d912 --- /dev/null +++ b/examples/testing.rs @@ -0,0 +1,176 @@ +use std::convert::Infallible; +use std::error::Error; +use std::fmt::Debug; + +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::DeserializeTagged; + +fn main() -> Result<(), Box<dyn Error>> +{ +    let mut deserializer = BufferedDeserializer::new( +        concat!("<food><name>Banana</name><kind>Fruit</kind></food>",).as_bytes(), +    ); + +    let food = deserializer.de_tag::<Food>("food", IgnoreEnd::No)?; + +    println!("Food {} with kind {:?}", food.name, food.kind); + +    Ok(()) +} + +#[derive(Debug)] +struct Food +{ +    name: String, +    kind: FoodKind, +} + +impl DeserializeTagged for Food +{ +    type Error = FoodError; + +    fn deserialize<TDeserializer: Deserializer>( +        _start: &TagStart, +        deserializer: &mut TDeserializer, +    ) -> Result<Self, Self::Error> +    { +        let name = +            deserializer.de_tag_with("name", IgnoreEnd::No, |_, deserializer| { +                deserializer.de_text() +            })?; + +        let kind = +            deserializer.de_tag_with("kind", IgnoreEnd::No, |_, deserializer| { +                match deserializer.de_text()?.as_str() { +                    "Fruit" => Ok(FoodKind::Fruit), +                    "Vegetable" => Ok(FoodKind::Vegetable), +                    unknown_kind => { +                        Err(FoodError::UnknownFoodKind(unknown_kind.to_string())) +                    } +                } +            })?; + +        Ok(Self { name, kind }) +    } +} + +#[derive(Debug)] +enum FoodKind +{ +    Fruit, +    Vegetable, +} + +#[derive(Debug, thiserror::Error)] +enum FoodError +{ +    #[error("Unknown food kind '{0}'")] +    UnknownFoodKind(String), + +    #[error(transparent)] +    DeserializeFailed(#[from] DeserializerError<Infallible>), +} + +impl<DeError: Into<Self>> From<DeserializerError<DeError>> for FoodError +{ +    fn from(err: DeserializerError<DeError>) -> Self +    { +        if let DeserializerError::DeserializeFailed(de_err) = err { +            return de_err.into(); +        } + +        err.into_never_de_err().into() +    } +} + +#[cfg(test)] +mod tests +{ +    use mockall::mock; +    use mockall::predicate::{always, eq}; +    use xml_stinks::deserializer::{Error, MaybeStatic}; + +    use super::*; + +    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>>; +        } +    } + +    #[test] +    fn deserialize_food_works() +    { +        let mut mock_deserializer = MockDeserializer::new(); + +        mock_deserializer +            .expect_de_tag_with::<String, DeserializerError<Infallible>>() +            .with(eq("name"), eq(IgnoreEnd::No), always()) +            .returning(|tag_name, _, func| { +                let mut deserializer = MockDeserializer::new(); + +                deserializer +                    .expect_de_text() +                    .returning(|| Ok("Carrot".to_string())) +                    .once(); + +                Ok(func(&TagStart::new(tag_name), &mut deserializer)?) +            }) +            .once(); + +        mock_deserializer +            .expect_de_tag_with::<FoodKind, FoodError>() +            .with(eq("kind"), eq(IgnoreEnd::No), always()) +            .returning(|tag_name, _, func| { +                let mut deserializer = MockDeserializer::new(); + +                deserializer +                    .expect_de_text() +                    .returning(|| Ok("Vegetable".to_string())) +                    .once(); + +                Ok(func(&TagStart::new(tag_name), &mut deserializer)?) +            }) +            .once(); + +        let food = Food::deserialize(&TagStart::new("food"), &mut mock_deserializer) +            .expect("Expected Ok"); + +        assert_eq!(food.name, "Carrot"); +        assert!(matches!(food.kind, FoodKind::Vegetable)); +    } +} | 
