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> { let mut deserializer = BufferedDeserializer::new( concat!("BananaFruit",).as_bytes(), ); let food = deserializer.de_tag::("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( _start: &TagStart, deserializer: &mut TDeserializer, ) -> Result { 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), } impl> From> for FoodError { fn from(err: DeserializerError) -> 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( &mut self, tag_name: &str, ignore_end: IgnoreEnd, ) -> Result>; fn de_tag_with( &mut self, tag_name: &str, ignore_end: IgnoreEnd, deserialize: Func, ) -> Result> where TOutput: MaybeStatic, Err: std::error::Error + Send + Sync + 'static, Func: FnOnce(&TagStart, &mut MockDeserializer) -> Result + MaybeStatic; fn de_tag_list( &mut self, tag_name: Option ) -> Result, Error> where De: DeserializeTagged, TagName: AsRef + MaybeStatic; fn de_text(&mut self) -> Result>; fn skip_to_tag_start(&mut self, tag_name: &str) -> Result<(), Error>; fn skip_to_tag_end(&mut self, tag_name: &str) -> Result<(), Error>; } } #[test] fn deserialize_food_works() { let mut mock_deserializer = MockDeserializer::new(); mock_deserializer .expect_de_tag_with::>() .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::() .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)); } }