When decoding maps, leave fields that weren't processed

Fixes an XXX
This commit is contained in:
Alex Crichton 2015-12-18 16:29:07 -08:00
parent 4987b47a92
commit 39d77b6d79
3 changed files with 71 additions and 28 deletions

View file

@ -1,5 +1,7 @@
use std::error;
use std::fmt;
use std::collections::{btree_map, BTreeMap};
use std::iter::Peekable;
use Value;
use self::DecodeErrorKind::*;
@ -17,6 +19,8 @@ pub struct Decoder {
/// whether fields were decoded or not.
pub toml: Option<Value>,
cur_field: Option<String>,
cur_map: Peekable<btree_map::IntoIter<String, Value>>,
leftover_map: ::Table,
}
/// Description for errors which can occur while decoding a type.
@ -105,7 +109,12 @@ impl Decoder {
/// This decoder can be passed to the `Decodable` methods or driven
/// manually.
pub fn new(toml: Value) -> Decoder {
Decoder { toml: Some(toml), cur_field: None }
Decoder {
toml: Some(toml),
cur_field: None,
leftover_map: BTreeMap::new(),
cur_map: BTreeMap::new().into_iter().peekable(),
}
}
fn sub_decoder(&self, toml: Option<Value>, field: &str) -> Decoder {
@ -118,7 +127,9 @@ impl Decoder {
None => Some(format!("{}", field)),
Some(ref s) => Some(format!("{}.{}", s, field))
}
}
},
leftover_map: BTreeMap::new(),
cur_map: BTreeMap::new().into_iter().peekable(),
}
}

View file

@ -1,5 +1,6 @@
use rustc_serialize;
use std::mem;
use std::collections::BTreeMap;
use super::{Decoder, DecodeError};
use super::DecodeErrorKind::*;
@ -275,46 +276,50 @@ impl rustc_serialize::Decoder for Decoder {
-> Result<T, DecodeError>
where F: FnOnce(&mut Decoder, usize) -> Result<T, DecodeError>
{
let len = match self.toml {
Some(Value::Table(ref table)) => table.len(),
ref found => return Err(self.mismatch("table", found)),
let map = match self.toml.take() {
Some(Value::Table(table)) => table,
found => {
self.toml = found;
return Err(self.mismatch("table", &self.toml))
}
};
let ret = try!(f(self, len));
self.toml.take();
let amt = map.len();
let prev_iter = mem::replace(&mut self.cur_map,
map.into_iter().peekable());
let prev_map = mem::replace(&mut self.leftover_map, BTreeMap::new());
let ret = try!(f(self, amt));
let leftover = mem::replace(&mut self.leftover_map, prev_map);
self.cur_map = prev_iter;
if leftover.len() > 0 {
self.toml = Some(Value::Table(leftover));
}
Ok(ret)
}
fn read_map_elt_key<T, F>(&mut self, idx: usize, f: F)
-> Result<T, DecodeError>
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
{
match self.toml {
Some(Value::Table(ref table)) => {
match table.iter().skip(idx).next() {
Some((key, _)) => {
let val = Value::String(key.to_string());
f(&mut self.sub_decoder(Some(val), key))
}
None => Err(self.err(ExpectedMapKey(idx))),
}
}
ref found => Err(self.mismatch("table", found)),
}
let key = match self.cur_map.peek().map(|p| p.0.clone()) {
Some(k) => k,
None => return Err(self.err(ExpectedMapKey(idx))),
};
let val = Value::String(key.clone());
f(&mut self.sub_decoder(Some(val), &key))
}
fn read_map_elt_val<T, F>(&mut self, idx: usize, f: F)
-> Result<T, DecodeError>
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
{
match self.toml {
Some(Value::Table(ref table)) => {
match table.iter().skip(idx).next() {
match self.cur_map.next() {
Some((key, value)) => {
// XXX: this shouldn't clone
f(&mut self.sub_decoder(Some(value.clone()), key))
let mut d = self.sub_decoder(Some(value), &key);
let ret = f(&mut d);
if let Some(toml) = d.toml.take() {
self.leftover_map.insert(key, toml);
}
None => Err(self.err(ExpectedMapElement(idx))),
return ret
}
}
ref found => Err(self.mismatch("table", found)),
None => return Err(self.err(ExpectedMapElement(idx))),
}
}

View file

@ -639,6 +639,33 @@ mod tests {
})));
}
#[test]
fn unused_fields8() {
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
struct Foo { a: BTreeMap<String, Bar> }
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
struct Bar { a: isize }
let v = Foo { a: map! { a, Bar { a: 2 } } };
let mut d = Decoder::new(Table(map! {
a, Table(map! {
a, Table(map! {
a, Integer(2),
b, Integer(2)
})
})
}));
assert_eq!(v, Decodable::decode(&mut d).unwrap());
assert_eq!(d.toml, Some(Table(map! {
a, Table(map! {
a, Table(map! {
b, Integer(2)
})
})
})));
}
#[test]
fn empty_arrays() {
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]