From 39d77b6d79dfbba6923f1529f63a3b6117bb6772 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 18 Dec 2015 16:29:07 -0800 Subject: When decoding maps, leave fields that weren't processed Fixes an XXX --- src/decoder/mod.rs | 15 +++++++++-- src/decoder/rustc_serialize.rs | 57 +++++++++++++++++++++++------------------- src/encoder/rustc_serialize.rs | 27 ++++++++++++++++++++ 3 files changed, 71 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 62de223..50a781c 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -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, cur_field: Option, + cur_map: Peekable>, + 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, 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(), } } diff --git a/src/decoder/rustc_serialize.rs b/src/decoder/rustc_serialize.rs index b84eeb9..9d2644e 100644 --- a/src/decoder/rustc_serialize.rs +++ b/src/decoder/rustc_serialize.rs @@ -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 where F: FnOnce(&mut Decoder, usize) -> Result { - 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(&mut self, idx: usize, f: F) -> Result where F: FnOnce(&mut Decoder) -> Result { - 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(&mut self, idx: usize, f: F) -> Result where F: FnOnce(&mut Decoder) -> Result { - match self.toml { - Some(Value::Table(ref table)) => { - match table.iter().skip(idx).next() { - Some((key, value)) => { - // XXX: this shouldn't clone - f(&mut self.sub_decoder(Some(value.clone()), key)) - } - None => Err(self.err(ExpectedMapElement(idx))), + match self.cur_map.next() { + Some((key, value)) => { + 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); } + return ret } - ref found => Err(self.mismatch("table", found)), + None => return Err(self.err(ExpectedMapElement(idx))), } } diff --git a/src/encoder/rustc_serialize.rs b/src/encoder/rustc_serialize.rs index ab5e90f..7f1db87 100644 --- a/src/encoder/rustc_serialize.rs +++ b/src/encoder/rustc_serialize.rs @@ -639,6 +639,33 @@ mod tests { }))); } + #[test] + fn unused_fields8() { + #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)] + struct Foo { a: BTreeMap } + #[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)] -- cgit v1.2.3