aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2023-05-11 21:27:36 +0200
committerHampusM <hampus@hampusmat.com>2023-05-11 21:27:36 +0200
commitbdce3a36f72de6097fb78a917e29b7e093a6ef58 (patch)
tree7d2cb765c857a3d78cee39c97b926e3b26ec2bd7 /examples
parente3a0736f8f73d310a8031fdd169467b7b26bc981 (diff)
docs: add examples
Diffstat (limited to 'examples')
-rw-r--r--examples/data.xml8
-rw-r--r--examples/simple.rs143
-rw-r--r--examples/testing.rs176
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));
+ }
+}