aboutsummaryrefslogtreecommitdiff
path: root/src/decoder
diff options
context:
space:
mode:
Diffstat (limited to 'src/decoder')
-rw-r--r--src/decoder/mod.rs205
-rw-r--r--src/decoder/rustc_serialize.rs311
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))
+ }
+ }
+}