use rustc_serialize; use std::mem; use super::{Decoder, DecodeError}; use super::DecodeErrorKind::*; use Value; impl rustc_serialize::Decoder for Decoder { type Error = DecodeError; fn read_nil(&mut self) -> Result<(), DecodeError> { match self.toml { Some(Value::String(ref s)) if s.len() == 0 => {} Some(Value::String(..)) => return Err(self.err(NilTooLong)), ref found => return Err(self.mismatch("string", found)), } self.toml.take(); Ok(()) } fn read_usize(&mut self) -> Result { self.read_i64().map(|i| i as usize) } fn read_u64(&mut self) -> Result { self.read_i64().map(|i| i as u64) } fn read_u32(&mut self) -> Result { self.read_i64().map(|i| i as u32) } fn read_u16(&mut self) -> Result { self.read_i64().map(|i| i as u16) } fn read_u8(&mut self) -> Result { self.read_i64().map(|i| i as u8) } fn read_isize(&mut self) -> Result { self.read_i64().map(|i| i as isize) } fn read_i64(&mut self) -> Result { match self.toml { Some(Value::Integer(i)) => { self.toml.take(); Ok(i) } ref found => Err(self.mismatch("integer", found)), } } fn read_i32(&mut self) -> Result { self.read_i64().map(|i| i as i32) } fn read_i16(&mut self) -> Result { self.read_i64().map(|i| i as i16) } fn read_i8(&mut self) -> Result { self.read_i64().map(|i| i as i8) } fn read_bool(&mut self) -> Result { match self.toml { Some(Value::Boolean(b)) => { self.toml.take(); Ok(b) } ref found => Err(self.mismatch("bool", found)), } } fn read_f64(&mut self) -> Result { match self.toml { Some(Value::Float(f)) => Ok(f), ref found => Err(self.mismatch("float", found)), } } fn read_f32(&mut self) -> Result { self.read_f64().map(|f| f as f32) } fn read_char(&mut self) -> Result { let ch = match self.toml { Some(Value::String(ref s)) if s.chars().count() == 1 => s.chars().next().unwrap(), ref found => return Err(self.mismatch("string", found)), }; self.toml.take(); Ok(ch) } fn read_str(&mut self) -> Result { match self.toml.take() { Some(Value::String(s)) => Ok(s), found => { let err = Err(self.mismatch("string", &found)); self.toml = found; err } } } // Compound types: fn read_enum(&mut self, _name: &str, f: F) -> Result where F: FnOnce(&mut Decoder) -> Result { f(self) } fn read_enum_variant(&mut self, names: &[&str], mut f: F) -> Result where F: FnMut(&mut Decoder, usize) -> Result { // When decoding enums, this crate takes the strategy of trying to // decode the current TOML as all of the possible variants, returning // success on the first one that succeeds. // // Note that fidelity of the errors returned here is a little nebulous, // but we try to return the error that had the relevant field as the // longest field. This way we hopefully match an error against what was // most likely being written down without losing too much info. let mut first_error = None::; for i in 0..names.len() { let mut d = self.sub_decoder(self.toml.clone(), ""); match f(&mut d, i) { Ok(t) => { self.toml = d.toml; return Ok(t) } Err(e) => { if let Some(ref first) = first_error { let my_len = e.field.as_ref().map(|s| s.len()); let first_len = first.field.as_ref().map(|s| s.len()); if my_len <= first_len { continue } } first_error = Some(e); } } } Err(first_error.unwrap_or_else(|| self.err(NoEnumVariants))) } fn read_enum_variant_arg(&mut self, _a_idx: usize, f: F) -> Result where F: FnOnce(&mut Decoder) -> Result { f(self) } fn read_enum_struct_variant(&mut self, _names: &[&str], _f: F) -> Result where F: FnMut(&mut Decoder, usize) -> Result { panic!() } fn read_enum_struct_variant_field(&mut self, _f_name: &str, _f_idx: usize, _f: F) -> Result where F: FnOnce(&mut Decoder) -> Result { panic!() } fn read_struct(&mut self, _s_name: &str, _len: usize, f: F) -> Result where F: FnOnce(&mut Decoder) -> Result { match self.toml { Some(Value::Table(..)) => { let ret = try!(f(self)); match self.toml { Some(Value::Table(ref t)) if t.len() == 0 => {} _ => return Ok(ret) } self.toml.take(); Ok(ret) } ref found => Err(self.mismatch("table", found)), } } fn read_struct_field(&mut self, f_name: &str, _f_idx: usize, f: F) -> Result where F: FnOnce(&mut Decoder) -> Result { let field = format!("{}", f_name); let toml = match self.toml { Some(Value::Table(ref mut table)) => { table.remove(&field) .or_else(|| table.remove(&f_name.replace("_", "-"))) }, ref found => return Err(self.mismatch("table", found)), }; let mut d = self.sub_decoder(toml, f_name); let ret = try!(f(&mut d)); if let Some(value) = d.toml { if let Some(Value::Table(ref mut table)) = self.toml { table.insert(field, value); } } Ok(ret) } fn read_tuple(&mut self, tuple_len: usize, f: F) -> Result where F: FnOnce(&mut Decoder) -> Result { self.read_seq(move |d, len| { assert!(len == tuple_len, "expected tuple of length `{}`, found tuple \ of length `{}`", tuple_len, len); f(d) }) } fn read_tuple_arg(&mut self, a_idx: usize, f: F) -> Result where F: FnOnce(&mut Decoder) -> Result { self.read_seq_elt(a_idx, f) } fn read_tuple_struct(&mut self, _s_name: &str, _len: usize, _f: F) -> Result where F: FnOnce(&mut Decoder) -> Result { panic!() } fn read_tuple_struct_arg(&mut self, _a_idx: usize, _f: F) -> Result where F: FnOnce(&mut Decoder) -> Result { panic!() } // Specialized types: fn read_option(&mut self, mut f: F) -> Result where F: FnMut(&mut Decoder, bool) -> Result { match self.toml { Some(..) => f(self, true), None => f(self, false), } } fn read_seq(&mut self, f: F) -> Result where F: FnOnce(&mut Decoder, usize) -> Result { let len = match self.toml { Some(Value::Array(ref arr)) => arr.len(), None => 0, ref found => return Err(self.mismatch("array", found)), }; let ret = try!(f(self, len)); match self.toml { Some(Value::Array(ref mut arr)) => { arr.retain(|slot| slot.as_integer() != Some(0)); if arr.len() != 0 { return Ok(ret) } } _ => return Ok(ret) } self.toml.take(); Ok(ret) } fn read_seq_elt(&mut self, idx: usize, f: F) -> Result where F: FnOnce(&mut Decoder) -> Result { let toml = match self.toml { Some(Value::Array(ref mut arr)) => { mem::replace(&mut arr[idx], Value::Integer(0)) } ref found => return Err(self.mismatch("array", found)), }; let mut d = self.sub_decoder(Some(toml), ""); let ret = try!(f(&mut d)); match d.toml { Some(toml) => match self.toml { Some(Value::Array(ref mut arr)) => arr[idx] = toml, _ => {} }, _ => {} } Ok(ret) } fn read_map(&mut self, f: F) -> 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 ret = try!(f(self, len)); self.toml.take(); 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)), } } 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))), } } ref found => Err(self.mismatch("table", found)), } } fn error(&mut self, err: &str) -> DecodeError { DecodeError { field: self.cur_field.clone(), kind: ApplicationError(format!("{}", err)) } } } #[cfg(test)] mod tests { use rustc_serialize::Decodable; use std::collections::HashMap; use {Parser, Decoder, Value}; #[test] fn bad_enum_chooses_longest_error() { #[derive(RustcDecodable)] #[allow(dead_code)] struct Foo { wut: HashMap, } #[derive(RustcDecodable)] enum Bar { Simple(String), Detailed(Baz), } #[derive(RustcDecodable, Debug)] struct Baz { features: Vec, } let s = r#" [wut] a = { features = "" } "#; let v = Parser::new(s).parse().unwrap(); let mut d = Decoder::new(Value::Table(v)); let err = match Foo::decode(&mut d) { Ok(_) => panic!("expected error"), Err(e) => e, }; assert_eq!(err.field.as_ref().unwrap(), "wut.a.features"); } }