Skip to content

Commit

Permalink
Merge pull request #12 from indiv0/tests-states-out-of-order
Browse files Browse the repository at this point in the history
Add custom deserializer for `States` to handle out of order elements
  • Loading branch information
indiv0 authored Dec 17, 2016
2 parents 5ef365b + 594e948 commit 7867fce
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 1 deletion.
219 changes: 218 additions & 1 deletion src/model.in.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -426,3 +426,220 @@ pub struct Error {
pub code: u32,
pub msg: String,
}

impl Deserialize for States {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
where D: Deserializer,
{
enum Field { Count, State, Statelist };

impl Deserialize for Field {
#[inline]
fn deserialize<D>(deserializer: &mut D) -> Result<Field, D::Error>
where D: Deserializer,
{
struct FieldVisitor;

impl Visitor for FieldVisitor {
type Value = Field;

fn visit_usize<E>(&mut self, value: usize) -> Result<Field, E>
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<E>(&mut self, value: &str) -> Result<Field, E>
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<E>(&mut self, value: &[u8]) -> Result<Field, E>
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<V>(&mut self, mut visitor: V) -> Result<States, V::Error>
where V: SeqVisitor,
{
let count = match match visitor.visit::<u32>() {
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::<Vec<State>>() {
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::<Option<Vec<Statelist>>>() {
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<V>(&mut self, mut visitor: V) -> Result<States, V::Error>
where V: MapVisitor,
{
let mut count: Option<u32> = None;
let mut state: Option<Vec<State>> = None;
let mut statelist: Option<Option<Vec<Statelist>>> = None;
while let Some(key) = match visitor.visit_key::<Field>() {
Result::Ok(val) => val,
Result::Err(err) => return Result::Err(From::from(err)),
} {
match key {
Field::Count => {
if count.is_some() {
return Err(<V::Error as
SerdeError>::duplicate_field("count"));
}
count = Some(match visitor.visit_value::<u32>() {
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::<Vec<State>>() {
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::<Option<Vec<Statelist>>>() {
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)
}
}
47 changes: 47 additions & 0 deletions src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -68,6 +70,7 @@ mod tests {
from_str::<QueryResult>(&read_sample_data_from_path("tests/sample-data/query_result_4.xml")).unwrap();
from_str::<QueryResult>(&read_sample_data_from_path("tests/sample-data/query_result_5.xml")).unwrap();
from_str::<QueryResult>(&read_sample_data_from_path("tests/sample-data/query_result_6.xml")).unwrap();
from_str::<QueryResult>(&read_sample_data_from_path("tests/sample-data/query_result_7.xml")).unwrap();
}

#[test]
Expand Down Expand Up @@ -113,12 +116,56 @@ mod tests {
#[test]
fn test_state_deserializer() {
from_str::<State>(&read_sample_data_from_path("tests/sample-data/state/state.xml")).unwrap();
}

#[test]
fn test_statelist_deserializer() {
from_str::<Statelist>(&read_sample_data_from_path("tests/sample-data/state/statelist.xml")).unwrap();
}

#[test]
fn test_states_deserializer() {
from_str::<States>(&read_sample_data_from_path("tests/sample-data/state/states.xml")).unwrap();
from_str::<States>(&read_sample_data_from_path("tests/sample-data/state/states-multiple-states.xml")).unwrap();
from_str::<States>(&read_sample_data_from_path("tests/sample-data/state/states-multiple-statelists.xml")).unwrap();
from_str::<States>(&read_sample_data_from_path("tests/sample-data/state/states-out-of-order.xml")).unwrap();
assert_eq!(
from_str::<States>(&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]
Expand Down
17 changes: 17 additions & 0 deletions tests/sample-data/query_result_7.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<queryresult success='false'
error='false'
numpods='0'
datatypes=''
timedout=''
timedoutpods=''
timing='4.739'
parsetiming='0.14400000000000002'
parsetimedout='false'
recalculate=''
id=''
host='http://www5b.wolframalpha.com'
server='35'
related=''
version='2.6'>
<languagemsg english='Wolfram|Alpha does not yet support Slovak.' />
</queryresult>
17 changes: 17 additions & 0 deletions tests/sample-data/state/states-out-of-order-complex.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<states count='5'>
<state name='a' input='a' />
<statelist count='2' value='b' delimiters=''>
<state name='b1' input='b1' />
<state name='b2' input='b2' />
</statelist>
<state name='c' input='c' />
<statelist count='2' value='d' delimiters=''>
<state name='d1' input='d1' />
<state name='d2' input='d2' />
</statelist>
<statelist count='2' value='e' delimiters=''>
<state name='e1' input='e1' />
<state name='e2' input='e2' />
</statelist>
<state name='f' input='f' />
</states>
14 changes: 14 additions & 0 deletions tests/sample-data/state/states-out-of-order.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<states count='3'>
<state name='Show synonym network'
input='Synonyms:WordData__Show synonym network' />
<statelist count='2'
value='Meanings combined'
delimiters=''>
<state name='Meanings divided'
input='Synonyms:WordData__Meanings divided' />
<state name='Meanings combined'
input='Synonyms:WordData__Meanings combined' />
</statelist>
<state name='More'
input='Synonyms:WordData__More' />
</states>

0 comments on commit 7867fce

Please sign in to comment.