diff options
author | Alex Crichton <alex@alexcrichton.com> | 2015-04-02 17:04:17 -0700 |
---|---|---|
committer | Alex Crichton <alex@alexcrichton.com> | 2015-04-02 17:07:37 -0700 |
commit | 2d49247b92f74cf760202b7d1a08d61804f45bca (patch) | |
tree | e000d46eeda80b4cee5e5ac79a36f00970ca4a6a /src/decoder | |
parent | cd33b87bf9a7f1e08fc73c436db51def4850855a (diff) | |
download | milf-rs-2d49247b92f74cf760202b7d1a08d61804f45bca.tar.gz milf-rs-2d49247b92f74cf760202b7d1a08d61804f45bca.zip |
Modularize rustc-serialize support
* Add rustc-serialize as a default feature
* Make room for for serde support
Diffstat (limited to 'src/decoder')
-rw-r--r-- | src/decoder/mod.rs | 205 | ||||
-rw-r--r-- | src/decoder/rustc_serialize.rs | 311 |
2 files changed, 516 insertions, 0 deletions
diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs new file mode 100644 index 0000000..675e0ca --- /dev/null +++ b/src/decoder/mod.rs @@ -0,0 +1,205 @@ +use std::error; +use std::fmt; + +use Value; +use self::DecodeErrorKind::*; + +#[cfg(feature = "rustc-serialize")] mod rustc_serialize; + +/// A structure to transform TOML values into Rust values. +/// +/// This decoder implements the serialization `Decoder` interface, allowing +/// `Decodable` types to be generated by this decoder. The input is any +/// arbitrary TOML value. +pub struct Decoder { + /// The TOML value left over after decoding. This can be used to inspect + /// whether fields were decoded or not. + pub toml: Option<Value>, + cur_field: Option<String>, +} + +/// Description for errors which can occur while decoding a type. +#[derive(PartialEq, Debug)] +pub struct DecodeError { + /// Field that this error applies to. + pub field: Option<String>, + /// The type of error which occurred while decoding, + pub kind: DecodeErrorKind, +} + +/// Enumeration of possible errors which can occur while decoding a structure. +#[derive(PartialEq, Debug)] +pub enum DecodeErrorKind { + /// An error flagged by the application, e.g. value out of range + ApplicationError(String), + /// A field was expected, but none was found. + ExpectedField(/* type */ Option<&'static str>), + /// A field was found, but it had the wrong type. + ExpectedType(/* expected */ &'static str, /* found */ &'static str), + /// The nth map key was expected, but none was found. + ExpectedMapKey(usize), + /// The nth map element was expected, but none was found. + ExpectedMapElement(usize), + /// An enum decoding was requested, but no variants were supplied + NoEnumVariants, + /// The unit type was being decoded, but a non-zero length string was found + NilTooLong, + /// There was an error with the syntactical structure of the TOML. + SyntaxError, + /// The end of the TOML input was reached too soon + EndOfStream, +} + +/// Decodes a TOML value into a decodable type. +/// +/// This function will consume the given TOML value and attempt to decode it +/// into the type specified. If decoding fails, `None` will be returned. If a +/// finer-grained error is desired, then it is recommended to use `Decodable` +/// directly. +#[cfg(feature = "rustc-serialize")] +pub fn decode<T: ::rustc_serialize::Decodable>(toml: Value) -> Option<T> { + ::rustc_serialize::Decodable::decode(&mut Decoder::new(toml)).ok() +} + +/// Decodes a TOML value into a decodable type. +/// +/// This function will consume the given TOML value and attempt to decode it +/// into the type specified. If decoding fails, `None` will be returned. If a +/// finer-grained error is desired, then it is recommended to use `Decodable` +/// directly. +#[cfg(all(not(feature = "rustc-serialize"), feature = "serde"))] +pub fn decode<T: ::serde::Deserialize>(toml: Value) -> Option<T> { + ::serde::Deserialize::deserialize(&mut Decoder::new(toml)).ok() +} + +/// Decodes a string into a toml-encoded value. +/// +/// This function will parse the given string into a TOML value, and then parse +/// the TOML value into the desired type. If any error occurs `None` is return. +/// +/// If more fine-grained errors are desired, these steps should be driven +/// manually. +#[cfg(feature = "rustc-serialize")] +pub fn decode_str<T: ::rustc_serialize::Decodable>(s: &str) -> Option<T> { + ::Parser::new(s).parse().and_then(|t| decode(Value::Table(t))) +} + +/// Decodes a string into a toml-encoded value. +/// +/// This function will parse the given string into a TOML value, and then parse +/// the TOML value into the desired type. If any error occurs `None` is return. +/// +/// If more fine-grained errors are desired, these steps should be driven +/// manually. +#[cfg(all(not(feature = "rustc-serialize"), feature = "serde"))] +pub fn decode_str<T: ::serde::Deserialize>(s: &str) -> Option<T> { + ::Parser::new(s).parse().and_then(|t| decode(Value::Table(t))) +} + +impl Decoder { + /// Creates a new decoder, consuming the TOML value to decode. + /// + /// 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 } + } + + fn sub_decoder(&self, toml: Option<Value>, field: &str) -> Decoder { + Decoder { + toml: toml, + cur_field: if field.len() == 0 { + self.cur_field.clone() + } else { + match self.cur_field { + None => Some(format!("{}", field)), + Some(ref s) => Some(format!("{}.{}", s, field)) + } + } + } + } + + fn err(&self, kind: DecodeErrorKind) -> DecodeError { + DecodeError { + field: self.cur_field.clone(), + kind: kind, + } + } + + fn mismatch(&self, expected: &'static str, + found: &Option<Value>) -> DecodeError{ + match *found { + Some(ref val) => self.err(ExpectedType(expected, val.type_str())), + None => self.err(ExpectedField(Some(expected))), + } + } +} + +impl fmt::Display for DecodeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(match self.kind { + ApplicationError(ref err) => { + write!(f, "{}", err) + } + ExpectedField(expected_type) => { + match expected_type { + Some("table") => write!(f, "expected a section"), + Some(e) => write!(f, "expected a value of type `{}`", e), + None => write!(f, "expected a value"), + } + } + ExpectedType(expected, found) => { + fn humanize(s: &str) -> String { + if s == "section" { + format!("a section") + } else { + format!("a value of type `{}`", s) + } + } + write!(f, "expected {}, but found {}", + humanize(expected), + humanize(found)) + } + ExpectedMapKey(idx) => { + write!(f, "expected at least {} keys", idx + 1) + } + ExpectedMapElement(idx) => { + write!(f, "expected at least {} elements", idx + 1) + } + NoEnumVariants => { + write!(f, "expected an enum variant to decode to") + } + NilTooLong => { + write!(f, "expected 0-length string") + } + SyntaxError => { + write!(f, "syntax error") + } + EndOfStream => { + write!(f, "end of stream") + } + }); + match self.field { + Some(ref s) => { + write!(f, " for the key `{}`", s) + } + None => Ok(()) + } + } +} + +impl error::Error for DecodeError { + fn description(&self) -> &str { + match self.kind { + ApplicationError(ref s) => &**s, + ExpectedField(..) => "expected a field", + ExpectedType(..) => "expected a type", + ExpectedMapKey(..) => "expected a map key", + ExpectedMapElement(..) => "expected a map element", + NoEnumVariants => "no enum variants to decode to", + NilTooLong => "nonzero length string representing nil", + SyntaxError => "syntax error", + EndOfStream => "end of stream", + } + } +} diff --git a/src/decoder/rustc_serialize.rs b/src/decoder/rustc_serialize.rs new file mode 100644 index 0000000..6e8fe59 --- /dev/null +++ b/src/decoder/rustc_serialize.rs @@ -0,0 +1,311 @@ +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<usize, DecodeError> { + self.read_i64().map(|i| i as usize) + } + fn read_u64(&mut self) -> Result<u64, DecodeError> { + self.read_i64().map(|i| i as u64) + } + fn read_u32(&mut self) -> Result<u32, DecodeError> { + self.read_i64().map(|i| i as u32) + } + fn read_u16(&mut self) -> Result<u16, DecodeError> { + self.read_i64().map(|i| i as u16) + } + fn read_u8(&mut self) -> Result<u8, DecodeError> { + self.read_i64().map(|i| i as u8) + } + fn read_isize(&mut self) -> Result<isize, DecodeError> { + self.read_i64().map(|i| i as isize) + } + fn read_i64(&mut self) -> Result<i64, DecodeError> { + 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<i32, DecodeError> { + self.read_i64().map(|i| i as i32) + } + fn read_i16(&mut self) -> Result<i16, DecodeError> { + self.read_i64().map(|i| i as i16) + } + fn read_i8(&mut self) -> Result<i8, DecodeError> { + self.read_i64().map(|i| i as i8) + } + fn read_bool(&mut self) -> Result<bool, DecodeError> { + 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<f64, DecodeError> { + match self.toml { + Some(Value::Float(f)) => Ok(f), + ref found => Err(self.mismatch("float", found)), + } + } + fn read_f32(&mut self) -> Result<f32, DecodeError> { + self.read_f64().map(|f| f as f32) + } + fn read_char(&mut self) -> Result<char, DecodeError> { + 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<String, DecodeError> { + 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<T, F>(&mut self, _name: &str, f: F) + -> Result<T, DecodeError> + where F: FnOnce(&mut Decoder) -> Result<T, DecodeError> + { + f(self) + } + + fn read_enum_variant<T, F>(&mut self, names: &[&str], mut f: F) + -> Result<T, DecodeError> + where F: FnMut(&mut Decoder, usize) -> Result<T, DecodeError> + { + 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 first_error.is_none() { + first_error = Some(e); + } + } + } + } + Err(first_error.unwrap_or_else(|| self.err(NoEnumVariants))) + } + fn read_enum_variant_arg<T, F>(&mut self, _a_idx: usize, f: F) + -> Result<T, DecodeError> + where F: FnOnce(&mut Decoder) -> Result<T, DecodeError> + { + f(self) + } + + fn read_enum_struct_variant<T, F>(&mut self, _names: &[&str], _f: F) + -> Result<T, DecodeError> + where F: FnMut(&mut Decoder, usize) -> Result<T, DecodeError> + { + panic!() + } + fn read_enum_struct_variant_field<T, F>(&mut self, + _f_name: &str, + _f_idx: usize, + _f: F) + -> Result<T, DecodeError> + where F: FnOnce(&mut Decoder) -> Result<T, DecodeError> + { + panic!() + } + + fn read_struct<T, F>(&mut self, _s_name: &str, _len: usize, f: F) + -> Result<T, DecodeError> + where F: FnOnce(&mut Decoder) -> Result<T, DecodeError> + { + 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<T, F>(&mut self, f_name: &str, _f_idx: usize, f: F) + -> Result<T, DecodeError> + where F: FnOnce(&mut Decoder) -> Result<T, DecodeError> + { + 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<T, F>(&mut self, tuple_len: usize, f: F) + -> Result<T, DecodeError> + where F: FnOnce(&mut Decoder) -> Result<T, DecodeError> + { + 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<T, F>(&mut self, a_idx: usize, f: F) + -> Result<T, DecodeError> + where F: FnOnce(&mut Decoder) -> Result<T, DecodeError> + { + self.read_seq_elt(a_idx, f) + } + + fn read_tuple_struct<T, F>(&mut self, _s_name: &str, _len: usize, _f: F) + -> Result<T, DecodeError> + where F: FnOnce(&mut Decoder) -> Result<T, DecodeError> + { + panic!() + } + fn read_tuple_struct_arg<T, F>(&mut self, _a_idx: usize, _f: F) + -> Result<T, DecodeError> + where F: FnOnce(&mut Decoder) -> Result<T, DecodeError> + { + panic!() + } + + // Specialized types: + fn read_option<T, F>(&mut self, mut f: F) + -> Result<T, DecodeError> + where F: FnMut(&mut Decoder, bool) -> Result<T, DecodeError> + { + match self.toml { + Some(..) => f(self, true), + None => f(self, false), + } + } + + fn read_seq<T, F>(&mut self, f: F) -> Result<T, DecodeError> + where F: FnOnce(&mut Decoder, usize) -> Result<T, DecodeError> + { + 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<T, F>(&mut self, idx: usize, f: F) + -> Result<T, DecodeError> + where F: FnOnce(&mut Decoder) -> Result<T, DecodeError> + { + 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<T, F>(&mut self, f: F) + -> 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 ret = try!(f(self, len)); + self.toml.take(); + 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(format!("{}", key)); + 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<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((_, value)) => { + // XXX: this shouldn't clone + f(&mut self.sub_decoder(Some(value.clone()), "")) + } + 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)) + } + } +} |