diff --git a/src/model.in.rs b/src/model.in.rs index 11c115e..f8798db 100644 --- a/src/model.in.rs +++ b/src/model.in.rs @@ -255,7 +255,7 @@ pub struct Pod { } /// A series of `State` attributes. -#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct States { // Attributes pub count: u32, @@ -426,3 +426,220 @@ pub struct Error { pub code: u32, pub msg: String, } + +impl Deserialize for States { + fn deserialize(deserializer: &mut D) -> Result + where D: Deserializer, + { + enum Field { Count, State, Statelist }; + + impl Deserialize for Field { + #[inline] + fn deserialize(deserializer: &mut D) -> Result + where D: Deserializer, + { + struct FieldVisitor; + + impl Visitor for FieldVisitor { + type Value = Field; + + fn visit_usize(&mut self, value: usize) -> Result + where E: SerdeError, + { + match value { + 0usize => { Ok(Field::Count) }, + 1usize => { Ok(Field::State) }, + 2usize => { Ok(Field::Statelist) }, + _ => Err(SerdeError::unknown_field(value.to_string().as_ref())), + } + } + + fn visit_str(&mut self, value: &str) -> Result + where E: SerdeError, + { + match value { + "count" => { Ok(Field::Count) }, + "state" => { Ok(Field::State) }, + "statelist" => { Ok(Field::Statelist) }, + _ => Err(SerdeError::unknown_field(value)), + } + } + + fn visit_bytes(&mut self, value: &[u8]) -> Result + where E: SerdeError, + { + match value { + b"count" => { Ok(Field::Count) }, + b"state" => { Ok(Field::State) }, + b"statelist" => { Ok(Field::Statelist) }, + _ => Err(SerdeError::unknown_field(String::from_utf8_lossy(value).as_ref())), + } + } + } + + deserializer.deserialize_struct_field(FieldVisitor) + } + } + + struct StatesVisitor; + + // TODO: remove all try!s + impl Visitor for StatesVisitor { + type Value = States; + + #[inline] + fn visit_seq(&mut self, mut visitor: V) -> Result + where V: SeqVisitor, + { + let count = match match visitor.visit::() { + Result::Ok(val) => val, + Result::Err(err) => return Result::Err(From::from(err)), + } { + Some(value) => value, + None => { + match visitor.end() { + Result::Ok(val) => val, + Result::Err(err) => return Result::Err(From::from(err)), + }; + return Err(SerdeError::invalid_length(0usize)); + }, + }; + let state = match match visitor.visit::>() { + Result::Ok(val) => val, + Result::Err(err) => return Result::Err(From::from(err)), + } { + Some(value) => value, + None => { + match visitor.end() { + Result::Ok(val) => val, + Result::Err(err) => return Result::Err(From::from(err)), + }; + return Err(SerdeError::invalid_length(1usize)); + }, + }; + let statelist = match match visitor.visit::>>() { + Result::Ok(val) => val, + Result::Err(err) => return Result::Err(From::from(err)), + } { + Some(value) => value, + None => { + match visitor.end() { + Result::Ok(val) => val, + Result::Err(err) => return Result::Err(From::from(err)), + }; + return Err(SerdeError::invalid_length(2usize)); + }, + }; + match visitor.end() { + Result::Ok(val) => val, + Result::Err(err) => return Result::Err(From::from(err)), + }; + Ok(States{ + count: count, + state: state, + statelist: statelist, + }) + } + + #[inline] + fn visit_map(&mut self, mut visitor: V) -> Result + where V: MapVisitor, + { + let mut count: Option = None; + let mut state: Option> = None; + let mut statelist: Option>> = None; + while let Some(key) = match visitor.visit_key::() { + Result::Ok(val) => val, + Result::Err(err) => return Result::Err(From::from(err)), + } { + match key { + Field::Count => { + if count.is_some() { + return Err(::duplicate_field("count")); + } + count = Some(match visitor.visit_value::() { + Result::Ok(val) => val, + Result::Err(err) => return Result::Err(From::from(err)), + }); + }, + // The next two match arms alone have been modified from + // the original, generated deserializer in order to + // support out of order elements. + Field::State => { + let more_state = Some(match visitor.visit_value::>() { + Result::Ok(val) => val, + Result::Err(err) => return Result::Err(From::from(err)), + }); + if let Some(mut more_state) = more_state { + if state.is_none() { + state = Some(more_state); + } else { + if let Some(mut state) = state.as_mut() { + state.append(&mut more_state); + } + } + } + }, + Field::Statelist => { + let more_statelist = Some(match visitor.visit_value::>>() { + Result::Ok(val) => val, + Result::Err(err) => return Result::Err(From::from(err)), + }); + if let Some(more_statelist) = more_statelist { + if let Some(mut more_statelist) = more_statelist { + if statelist.is_none() { + statelist = Some(Some(more_statelist)); + } else { + if let Some(mut statelist) = statelist.as_mut() { + if let Some(statelist) = statelist.as_mut() { + statelist.append(&mut more_statelist); + } + } + } + } + } + }, + } + } + match visitor.end() { + Result::Ok(val) => val, + Result::Err(err) => return Result::Err(From::from(err)), + }; + let count = match count { + Some(count) => count, + None => match visitor.missing_field("count") { + Result::Ok(val) => val, + Result::Err(err) => return Result::Err(From::from(err)), + }, + }; + let state = match state { + Some(state) => state, + None => match visitor.missing_field("state") { + Result::Ok(val) => val, + Result::Err(err) => return Result::Err(From::from(err)), + }, + }; + let statelist = match statelist { + Some(statelist) => statelist, + None => match visitor.missing_field("statelist") { + Result::Ok(val) => val, + Result::Err(err) => return Result::Err(From::from(err)), + }, + }; + Ok(States{ + count: count, + state: state, + statelist: statelist, + }) + } + } + + const FIELDS: &'static [&'static str] = &[ + "count", + "state", + "statelist", + ]; + deserializer.deserialize_struct("States", FIELDS, StatesVisitor) + } +} diff --git a/src/model.rs b/src/model.rs index 8b43ca9..9466f1a 100644 --- a/src/model.rs +++ b/src/model.rs @@ -15,6 +15,8 @@ //! For more information, see [Wolfram|Alpha's API //! documentation](http://products.wolframalpha.com/api/documentation.html). +use serde::{Deserialize, Deserializer, Error as SerdeError}; +use serde::de::{MapVisitor, SeqVisitor, Visitor}; use url::Url; #[cfg(feature = "nightly")] @@ -68,6 +70,7 @@ mod tests { from_str::(&read_sample_data_from_path("tests/sample-data/query_result_4.xml")).unwrap(); from_str::(&read_sample_data_from_path("tests/sample-data/query_result_5.xml")).unwrap(); from_str::(&read_sample_data_from_path("tests/sample-data/query_result_6.xml")).unwrap(); + from_str::(&read_sample_data_from_path("tests/sample-data/query_result_7.xml")).unwrap(); } #[test] @@ -113,12 +116,56 @@ mod tests { #[test] fn test_state_deserializer() { from_str::(&read_sample_data_from_path("tests/sample-data/state/state.xml")).unwrap(); + } + #[test] + fn test_statelist_deserializer() { from_str::(&read_sample_data_from_path("tests/sample-data/state/statelist.xml")).unwrap(); + } + #[test] + fn test_states_deserializer() { from_str::(&read_sample_data_from_path("tests/sample-data/state/states.xml")).unwrap(); from_str::(&read_sample_data_from_path("tests/sample-data/state/states-multiple-states.xml")).unwrap(); from_str::(&read_sample_data_from_path("tests/sample-data/state/states-multiple-statelists.xml")).unwrap(); + from_str::(&read_sample_data_from_path("tests/sample-data/state/states-out-of-order.xml")).unwrap(); + assert_eq!( + from_str::(&read_sample_data_from_path("tests/sample-data/state/states-out-of-order-complex.xml")).unwrap(), + States { + count: 5, + state: vec![ + State { name: 'a'.to_string(), input: 'a'.to_string(), }, + State { name: 'c'.to_string(), input: 'c'.to_string(), }, + State { name: 'f'.to_string(), input: 'f'.to_string(), }, + ], + statelist: Some(vec![ + Statelist { + count: 2, + value: 'b'.to_string(), + state: vec![ + State { name: "b1".to_owned(), input: "b1".to_owned(), }, + State { name: "b2".to_owned(), input: "b2".to_owned(), }, + ], + }, + Statelist { + count: 2, + value: 'd'.to_string(), + state: vec![ + State { name: "d1".to_owned(), input: "d1".to_owned(), }, + State { name: "d2".to_owned(), input: "d2".to_owned(), }, + ], + }, + Statelist { + count: 2, + value: 'e'.to_string(), + state: vec![ + State { name: "e1".to_owned(), input: "e1".to_owned(), }, + State { name: "e2".to_owned(), input: "e2".to_owned(), }, + ], + }, + ]), + } + ); } #[test] diff --git a/tests/sample-data/query_result_7.xml b/tests/sample-data/query_result_7.xml new file mode 100644 index 0000000..c7bd709 --- /dev/null +++ b/tests/sample-data/query_result_7.xml @@ -0,0 +1,17 @@ + + + diff --git a/tests/sample-data/state/states-out-of-order-complex.xml b/tests/sample-data/state/states-out-of-order-complex.xml new file mode 100644 index 0000000..2ea83ea --- /dev/null +++ b/tests/sample-data/state/states-out-of-order-complex.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/tests/sample-data/state/states-out-of-order.xml b/tests/sample-data/state/states-out-of-order.xml new file mode 100644 index 0000000..7f34331 --- /dev/null +++ b/tests/sample-data/state/states-out-of-order.xml @@ -0,0 +1,14 @@ + + + + + + + +