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 /examples | |
parent | e3a0736f8f73d310a8031fdd169467b7b26bc981 (diff) |
docs: add examples
Diffstat (limited to 'examples')
-rw-r--r-- | examples/data.xml | 8 | ||||
-rw-r--r-- | examples/simple.rs | 143 | ||||
-rw-r--r-- | examples/testing.rs | 176 |
3 files changed, 327 insertions, 0 deletions
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)); + } +} |