]>
Commit | Line | Data |
---|---|---|
476ff2be SL |
1 | use std::error; |
2 | use std::fmt; | |
3 | use std::collections::{btree_map, BTreeMap}; | |
4 | use std::iter::Peekable; | |
5 | ||
6 | use Value; | |
7 | use self::DecodeErrorKind::*; | |
8 | ||
9 | #[cfg(feature = "rustc-serialize")] mod rustc_serialize; | |
10 | #[cfg(feature = "serde")] mod serde; | |
11 | ||
12 | /// A structure to transform TOML values into Rust values. | |
13 | /// | |
14 | /// This decoder implements the serialization `Decoder` interface, allowing | |
15 | /// `Decodable` types to be generated by this decoder. The input is any | |
16 | /// arbitrary TOML value. | |
17 | pub struct Decoder { | |
18 | /// The TOML value left over after decoding. This can be used to inspect | |
19 | /// whether fields were decoded or not. | |
20 | pub toml: Option<Value>, | |
21 | cur_field: Option<String>, | |
22 | cur_map: Peekable<btree_map::IntoIter<String, Value>>, | |
23 | leftover_map: ::Table, | |
24 | } | |
25 | ||
26 | /// Description for errors which can occur while decoding a type. | |
27 | #[derive(PartialEq, Debug)] | |
28 | pub struct DecodeError { | |
29 | /// Field that this error applies to. | |
30 | pub field: Option<String>, | |
31 | /// The type of error which occurred while decoding, | |
32 | pub kind: DecodeErrorKind, | |
33 | } | |
34 | ||
35 | /// Enumeration of possible errors which can occur while decoding a structure. | |
36 | #[derive(PartialEq, Debug)] | |
37 | pub enum DecodeErrorKind { | |
38 | /// An error flagged by the application, e.g. value out of range | |
39 | ApplicationError(String), | |
40 | /// A field was expected, but none was found. | |
41 | ExpectedField(/* type */ Option<&'static str>), | |
42 | /// A field was found, but it was not an expected one. | |
43 | UnknownField, | |
44 | /// A field was found, but it had the wrong type. | |
45 | ExpectedType(/* expected */ &'static str, /* found */ &'static str), | |
46 | /// The nth map key was expected, but none was found. | |
47 | ExpectedMapKey(usize), | |
48 | /// The nth map element was expected, but none was found. | |
49 | ExpectedMapElement(usize), | |
50 | /// An enum decoding was requested, but no variants were supplied | |
51 | NoEnumVariants, | |
52 | /// The unit type was being decoded, but a non-zero length string was found | |
53 | NilTooLong, | |
54 | /// There was an error with the syntactical structure of the TOML. | |
55 | SyntaxError, | |
56 | /// A custom error was generated when decoding. | |
57 | CustomError(String), | |
58 | /// The end of the TOML input was reached too soon | |
59 | EndOfStream, | |
60 | /// Produced by serde ... | |
61 | InvalidType(&'static str), | |
62 | } | |
63 | ||
64 | /// Decodes a TOML value into a decodable type. | |
65 | /// | |
66 | /// This function will consume the given TOML value and attempt to decode it | |
67 | /// into the type specified. If decoding fails, `None` will be returned. If a | |
68 | /// finer-grained error is desired, then it is recommended to use `Decodable` | |
69 | /// directly. | |
70 | #[cfg(feature = "rustc-serialize")] | |
71 | pub fn decode<T: ::rustc_serialize::Decodable>(toml: Value) -> Option<T> { | |
72 | ::rustc_serialize::Decodable::decode(&mut Decoder::new(toml)).ok() | |
73 | } | |
74 | ||
75 | /// Decodes a TOML value into a decodable type. | |
76 | /// | |
77 | /// This function will consume the given TOML value and attempt to decode it | |
78 | /// into the type specified. If decoding fails, `None` will be returned. If a | |
79 | /// finer-grained error is desired, then it is recommended to use `Decodable` | |
80 | /// directly. | |
81 | #[cfg(all(not(feature = "rustc-serialize"), feature = "serde"))] | |
82 | pub fn decode<T: ::serde::Deserialize>(toml: Value) -> Option<T> { | |
83 | ::serde::Deserialize::deserialize(&mut Decoder::new(toml)).ok() | |
84 | } | |
85 | ||
86 | /// Decodes a string into a toml-encoded value. | |
87 | /// | |
88 | /// This function will parse the given string into a TOML value, and then parse | |
89 | /// the TOML value into the desired type. If any error occurs, `None` is | |
90 | /// returned. | |
91 | /// | |
92 | /// If more fine-grained errors are desired, these steps should be driven | |
93 | /// manually. | |
94 | #[cfg(feature = "rustc-serialize")] | |
95 | pub fn decode_str<T: ::rustc_serialize::Decodable>(s: &str) -> Option<T> { | |
96 | ::Parser::new(s).parse().and_then(|t| decode(Value::Table(t))) | |
97 | } | |
98 | ||
99 | /// Decodes a string into a toml-encoded value. | |
100 | /// | |
101 | /// This function will parse the given string into a TOML value, and then parse | |
102 | /// the TOML value into the desired type. If any error occurs, `None` is | |
103 | /// returned. | |
104 | /// | |
105 | /// If more fine-grained errors are desired, these steps should be driven | |
106 | /// manually. | |
107 | #[cfg(all(not(feature = "rustc-serialize"), feature = "serde"))] | |
108 | pub fn decode_str<T: ::serde::Deserialize>(s: &str) -> Option<T> { | |
109 | ::Parser::new(s).parse().and_then(|t| decode(Value::Table(t))) | |
110 | } | |
111 | ||
112 | impl Decoder { | |
113 | /// Creates a new decoder, consuming the TOML value to decode. | |
114 | /// | |
115 | /// This decoder can be passed to the `Decodable` methods or driven | |
116 | /// manually. | |
117 | pub fn new(toml: Value) -> Decoder { | |
118 | Decoder { | |
119 | toml: Some(toml), | |
120 | cur_field: None, | |
121 | leftover_map: BTreeMap::new(), | |
122 | cur_map: BTreeMap::new().into_iter().peekable(), | |
123 | } | |
124 | } | |
125 | ||
126 | fn sub_decoder(&self, toml: Option<Value>, field: &str) -> Decoder { | |
127 | Decoder { | |
128 | toml: toml, | |
129 | cur_field: if field.len() == 0 { | |
130 | self.cur_field.clone() | |
131 | } else { | |
132 | match self.cur_field { | |
133 | None => Some(format!("{}", field)), | |
134 | Some(ref s) => Some(format!("{}.{}", s, field)) | |
135 | } | |
136 | }, | |
137 | leftover_map: BTreeMap::new(), | |
138 | cur_map: BTreeMap::new().into_iter().peekable(), | |
139 | } | |
140 | } | |
141 | ||
142 | fn err(&self, kind: DecodeErrorKind) -> DecodeError { | |
143 | DecodeError { | |
144 | field: self.cur_field.clone(), | |
145 | kind: kind, | |
146 | } | |
147 | } | |
148 | ||
149 | fn mismatch(&self, expected: &'static str, | |
150 | found: &Option<Value>) -> DecodeError{ | |
151 | match *found { | |
152 | Some(ref val) => self.err(ExpectedType(expected, val.type_str())), | |
153 | None => self.err(ExpectedField(Some(expected))), | |
154 | } | |
155 | } | |
156 | } | |
157 | ||
158 | impl fmt::Display for DecodeError { | |
159 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
160 | try!(match self.kind { | |
161 | ApplicationError(ref err) => { | |
162 | write!(f, "{}", err) | |
163 | } | |
164 | ExpectedField(expected_type) => { | |
165 | match expected_type { | |
166 | Some("table") => write!(f, "expected a section"), | |
167 | Some(e) => write!(f, "expected a value of type `{}`", e), | |
168 | None => write!(f, "expected a value"), | |
169 | } | |
170 | } | |
171 | UnknownField => write!(f, "unknown field"), | |
172 | ExpectedType(expected, found) => { | |
173 | fn humanize(s: &str) -> String { | |
174 | if s == "section" { | |
175 | format!("a section") | |
176 | } else { | |
177 | format!("a value of type `{}`", s) | |
178 | } | |
179 | } | |
180 | write!(f, "expected {}, but found {}", | |
181 | humanize(expected), | |
182 | humanize(found)) | |
183 | } | |
184 | ExpectedMapKey(idx) => { | |
185 | write!(f, "expected at least {} keys", idx + 1) | |
186 | } | |
187 | ExpectedMapElement(idx) => { | |
188 | write!(f, "expected at least {} elements", idx + 1) | |
189 | } | |
190 | NoEnumVariants => { | |
191 | write!(f, "expected an enum variant to decode to") | |
192 | } | |
193 | NilTooLong => { | |
194 | write!(f, "expected 0-length string") | |
195 | } | |
196 | SyntaxError => { | |
197 | write!(f, "syntax error") | |
198 | } | |
199 | EndOfStream => { | |
200 | write!(f, "end of stream") | |
201 | } | |
202 | InvalidType(s) => { | |
203 | write!(f, "invalid type: {}", s) | |
204 | } | |
205 | CustomError(ref s) => { | |
206 | write!(f, "custom error: {}", s) | |
207 | } | |
208 | }); | |
209 | match self.field { | |
210 | Some(ref s) => { | |
211 | write!(f, " for the key `{}`", s) | |
212 | } | |
213 | None => Ok(()) | |
214 | } | |
215 | } | |
216 | } | |
217 | ||
218 | impl error::Error for DecodeError { | |
219 | fn description(&self) -> &str { | |
220 | match self.kind { | |
221 | ApplicationError(ref s) => &**s, | |
222 | ExpectedField(..) => "expected a field", | |
223 | UnknownField => "found an unknown field", | |
224 | ExpectedType(..) => "expected a type", | |
225 | ExpectedMapKey(..) => "expected a map key", | |
226 | ExpectedMapElement(..) => "expected a map element", | |
227 | NoEnumVariants => "no enum variants to decode to", | |
228 | NilTooLong => "nonzero length string representing nil", | |
229 | SyntaxError => "syntax error", | |
230 | EndOfStream => "end of stream", | |
231 | InvalidType(..) => "invalid type", | |
232 | CustomError(..) => "custom error", | |
233 | } | |
234 | } | |
235 | } |