regex_decode/
decoder.rs

1use errors::*;
2use rustc_serialize as S;
3use regex as R;
4
5pub struct Decoder<'a> {
6    captures: R::Captures<'a>,
7    stack: Vec<String>,
8}
9
10impl<'a> Decoder<'a> {
11    pub fn new(captures: R::Captures<'a>) -> Decoder<'a> {
12        Decoder {
13            captures: captures,
14            stack: vec![],
15        }
16    }
17}
18
19macro_rules! read_primitive {
20    ($name:ident, $ty:ident) => {
21        fn $name(&mut self) -> Result<$ty> {
22            match self.stack.pop() {
23                None => Err("missing value".into()),
24                Some(value) => value.parse().chain_err(|| "failed to decode primitive")
25            }
26        }
27    }
28}
29
30impl<'a> S::Decoder for Decoder<'a> {
31    type Error = Error;
32
33    fn read_nil(&mut self) -> Result<()> {
34        unimplemented!();
35    }
36
37    read_primitive! { read_usize, usize }
38    read_primitive! { read_u64, u64 }
39    read_primitive! { read_u32, u32 }
40    read_primitive! { read_u16, u16 }
41    read_primitive! { read_u8, u8 }
42
43    read_primitive! { read_isize, isize }
44    read_primitive! { read_i64, i64 }
45    read_primitive! { read_i32, i32 }
46    read_primitive! { read_i16, i16 }
47    read_primitive! { read_i8, i8 }
48
49    read_primitive! { read_f64, f64 }
50    read_primitive! { read_f32, f32 }
51
52    fn read_bool(&mut self) -> Result<bool> {
53        unimplemented!();
54    }
55
56    fn read_char(&mut self) -> Result<char> {
57        match self.stack.pop() {
58            None => Err("missing value".into()),
59            Some(value) => {
60                let mut chars = value.chars();
61
62                let c = match chars.next() {
63                    None => return Err("missing value".into()),
64                    Some(c) => c,
65                };
66
67                match chars.next() {
68                    None => Ok(c),
69                    Some(_) => Err("extra characters found".into()),
70                }
71            }
72        }
73    }
74
75    fn read_str(&mut self) -> Result<String> {
76        match self.stack.pop() {
77            None => Err("missing value".into()),
78            Some(value) => Ok(value.into())
79        }
80    }
81
82    fn read_enum<T, F>(&mut self, _: &str, f: F) -> Result<T> where F: FnOnce(&mut Self) -> Result<T> {
83        f(self)
84    }
85
86    fn read_enum_variant<T, F>(&mut self, names: &[&str], mut f: F) -> Result<T> where F: FnMut(&mut Self, usize) -> Result<T> {
87        let name = match self.stack.pop() {
88            None => return Err("missing value".into()),
89            Some(name) => name,
90        };
91
92        let idx = match names.iter().position(|n| *n == name) {
93            Some(idx) => idx,
94            None => return Err(format!("unknown variant {}", name).into())
95        };
96        f(self, idx)
97    }
98
99    fn read_enum_variant_arg<T, F>(&mut self, _: usize, f: F) -> Result<T> where F: FnOnce(&mut Self) -> Result<T> {
100        f(self)
101    }
102
103    fn read_enum_struct_variant<T, F>(&mut self, names: &[&str], f: F) -> Result<T> where F: FnMut(&mut Self, usize) -> Result<T> {
104        self.read_enum_variant(names, f)
105    }
106
107    fn read_enum_struct_variant_field<T, F>(&mut self, _: &str, f_idx: usize, f: F) -> Result<T> where F: FnOnce(&mut Self) -> Result<T> {
108        self.read_enum_variant_arg(f_idx, f)
109    }
110
111    fn read_struct<T, F>(&mut self, _: &str, _: usize, f: F) -> Result<T> where F: FnOnce(&mut Self) -> Result<T> {
112        f(self)
113    }
114
115    fn read_struct_field<T, F>(&mut self, f_name: &str, _: usize, f: F) -> Result<T> where F: FnOnce(&mut Self) -> Result<T> {
116        match self.captures.name(f_name) {
117            None => Err("missing field name".into()),
118            Some(val) => {
119                self.stack.push(val.to_string());
120                f(self)
121            }
122        }
123    }
124
125    fn read_tuple<T, F>(&mut self, _: usize, f: F) -> Result<T> where F: FnOnce(&mut Self) -> Result<T> {
126        f(self)
127    }
128
129    fn read_tuple_arg<T, F>(&mut self, a_idx: usize, f: F) -> Result<T> where F: FnOnce(&mut Self) -> Result<T> {
130        // a_idx + 1 because capture 0 is the whole match
131        match self.captures.at(a_idx + 1) {
132            None => Err("missing tuple arg".into()),
133            Some(val) => {
134                self.stack.push(val.to_string());
135                f(self)
136            }
137        }
138    }
139
140    fn read_tuple_struct<T, F>(&mut self, _: &str, len: usize, f: F) -> Result<T> where F: FnOnce(&mut Self) -> Result<T> {
141        self.read_tuple(len, f)
142    }
143
144    fn read_tuple_struct_arg<T, F>(&mut self, a_idx: usize, f: F) -> Result<T> where F: FnOnce(&mut Self) -> Result<T> {
145        self.read_tuple_arg(a_idx, f)
146    }
147
148    fn read_option<T, F>(&mut self, mut f: F) -> Result<T> where F: FnMut(&mut Self, bool) -> Result<T> {
149        let value = self.stack.pop();
150
151        match value {
152            None => f(self, false),
153            Some(value) => {
154                self.stack.push(value);
155                f(self, true)
156            }
157        }
158    }
159
160    fn read_seq<T, F>(&mut self, f: F) -> Result<T> where F: FnOnce(&mut Self, usize) -> Result<T> {
161        let mut temp = vec![];
162        for val in self.captures.iter().skip(1) {
163            temp.push(val.unwrap().to_string());
164        }
165        temp.reverse();
166        let len = temp.len();
167        self.stack.extend(temp);
168        f(self, len)
169    }
170
171    fn read_seq_elt<T, F>(&mut self, _: usize, f: F) -> Result<T> where F: FnOnce(&mut Self) -> Result<T> {
172        f(self)
173    }
174
175    fn read_map<T, F>(&mut self, f: F) -> Result<T> where F: FnOnce(&mut Self, usize) -> Result<T> {
176        let len = self.captures.len() - 1;
177
178        for (key, value) in self.captures.iter_named() {
179            self.stack.push(value.unwrap().to_string());
180            self.stack.push(key.to_string());
181        }
182
183        f(self, len)
184    }
185
186    fn read_map_elt_key<T, F>(&mut self, _: usize, f: F) -> Result<T> where F: FnOnce(&mut Self) -> Result<T> {
187        f(self)
188    }
189
190    fn read_map_elt_val<T, F>(&mut self, _: usize, f: F) -> Result<T> where F: FnOnce(&mut Self) -> Result<T> {
191        f(self)
192    }
193
194    fn error(&mut self, err: &str) -> Self::Error {
195        err.into()
196    }
197}
198
199pub fn decode<T: S::Decodable>(regex: &R::Regex, string: &str) -> Result<T> {
200    match regex.captures(string) {
201        None => Err("regex failed to match against text".into()),
202        Some(captures) => {
203            let mut decoder = Decoder::new(captures);
204            S::Decodable::decode(&mut decoder)
205        }
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use std::collections::HashMap;
212    use regex as R;
213    use super::*;
214
215    #[test]
216    fn decode_struct_with_strings() {
217        #[derive(RustcDecodable)]
218        struct Capture {
219            pub title: String,
220            pub year: String,
221        }
222
223        let re = R::Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)")
224                           .unwrap();
225        let text = "Not my favorite movie: 'Citizen Kane' (1941).";
226
227        let val = decode::<Capture>(&re, &text).unwrap();
228
229        assert_eq!(&val.title, "Citizen Kane");
230        assert_eq!(&val.year, "1941");
231    }
232
233    #[test]
234    fn decode_struct_with_usize() {
235        #[derive(RustcDecodable)]
236        struct Capture {
237            pub title: String,
238            pub year: usize,
239        }
240
241        let re = R::Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)")
242                           .unwrap();
243        let text = "Not my favorite movie: 'Citizen Kane' (1941).";
244
245        let val = decode::<Capture>(&re, &text).unwrap();
246
247        assert_eq!(&val.title, "Citizen Kane");
248        assert_eq!(val.year, 1941);
249    }
250
251    #[test]
252    fn decode_struct_with_u64() {
253        #[derive(RustcDecodable)]
254        struct Capture {
255            pub title: String,
256            pub year: u64,
257        }
258
259        let re = R::Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)")
260                           .unwrap();
261        let text = "Not my favorite movie: 'Citizen Kane' (1941).";
262
263        let val = decode::<Capture>(&re, &text).unwrap();
264
265        assert_eq!(&val.title, "Citizen Kane");
266        assert_eq!(val.year, 1941);
267    }
268
269    #[test]
270    fn decode_struct_with_u32() {
271        #[derive(RustcDecodable)]
272        struct Capture {
273            pub title: String,
274            pub year: u32,
275        }
276
277        let re = R::Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)")
278                           .unwrap();
279        let text = "Not my favorite movie: 'Citizen Kane' (1941).";
280
281        let val = decode::<Capture>(&re, &text).unwrap();
282
283        assert_eq!(&val.title, "Citizen Kane");
284        assert_eq!(val.year, 1941);
285    }
286
287    #[test]
288    fn decode_struct_with_u16() {
289        #[derive(RustcDecodable)]
290        struct Capture {
291            pub title: String,
292            pub year: u16,
293        }
294
295        let re = R::Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)")
296                           .unwrap();
297        let text = "Not my favorite movie: 'Citizen Kane' (1941).";
298
299        let val = decode::<Capture>(&re, &text).unwrap();
300
301        assert_eq!(&val.title, "Citizen Kane");
302        assert_eq!(val.year, 1941);
303    }
304
305    #[test]
306    #[should_panic]
307    fn decode_struct_with_u8_too_large() {
308        #[derive(RustcDecodable)]
309        struct Capture {
310            pub title: String,
311            pub year: u8,
312        }
313
314        let re = R::Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)")
315                           .unwrap();
316        let text = "Not my favorite movie: 'Citizen Kane' (1941).";
317
318        // This will panic because 1941 won't fit into a u8
319        decode::<Capture>(&re, &text).unwrap();
320    }
321
322    #[test]
323    fn decode_struct_with_u8() {
324        #[derive(RustcDecodable)]
325        struct Capture {
326            pub title: String,
327            pub year: u8,
328        }
329
330        let re = R::Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{2})\)")
331                           .unwrap();
332        let text = "Not my favorite movie: 'Citizen Kane' (41).";
333
334        let val = decode::<Capture>(&re, &text).unwrap();
335
336        assert_eq!(&val.title, "Citizen Kane");
337        assert_eq!(val.year, 41);
338    }
339
340    #[test]
341    #[should_panic]
342    fn decode_struct_with_char_too_large() {
343        #[derive(RustcDecodable)]
344        struct Capture {
345            pub title: char,
346            pub year: usize,
347        }
348
349        let re = R::Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)")
350                           .unwrap();
351        let text = "Not my favorite movie: 'Citizen Kane' (1941).";
352
353        // This will panic because Citizen Kane won't fit into a char
354        decode::<Capture>(&re, &text).unwrap();
355    }
356
357    #[test]
358    fn decode_struct_with_char() {
359        #[derive(RustcDecodable)]
360        struct Capture {
361            pub title: char,
362            pub year: usize,
363        }
364
365        let re = R::Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)")
366                           .unwrap();
367        let text = "Not my favorite movie: 'C' (1941).";
368
369        let val = decode::<Capture>(&re, &text).unwrap();
370
371        assert_eq!(val.title, 'C');
372        assert_eq!(val.year, 1941);
373    }
374
375    #[test]
376    fn decode_struct_with_option() {
377        #[derive(RustcDecodable)]
378        struct Capture {
379            pub title: String,
380            pub year: Option<usize>,
381        }
382
383        let re = R::Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)")
384                           .unwrap();
385        let text = "Not my favorite movie: 'Citizen Kane' (1941).";
386
387        let val = decode::<Capture>(&re, &text).unwrap();
388
389        assert_eq!(val.title, "Citizen Kane");
390        assert_eq!(val.year.is_some(), true);
391        assert_eq!(val.year.unwrap(), 1941);
392    }
393
394    #[test]
395    fn decode_tuple() {
396        let re = R::Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)")
397                           .unwrap();
398        let text = "Not my favorite movie: 'Citizen Kane' (1941).";
399
400        let (title, year) = decode::<(String, usize)>(&re, &text).unwrap();
401
402        assert_eq!(title, "Citizen Kane");
403        assert_eq!(year, 1941);
404    }
405
406    #[test]
407    fn decode_vec_string() {
408        let re = R::Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)")
409                           .unwrap();
410        let text = "Not my favorite movie: 'Citizen Kane' (1941).";
411
412        let val = decode::<Vec<String>>(&re, &text).unwrap();
413
414        assert_eq!(val[0], "Citizen Kane");
415        assert_eq!(val[1], "1941");
416    }
417
418    #[test]
419    fn decode_hashmap() {
420        let re = R::Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)")
421                           .unwrap();
422        let text = "Not my favorite movie: 'Citizen Kane' (1941).";
423
424        let val = decode::<HashMap<String, String>>(&re, &text).unwrap();
425
426        assert_eq!(val.get("title").unwrap(), "Citizen Kane");
427        assert_eq!(val.get("year").unwrap(), "1941");
428    }
429
430    #[test]
431    fn decode_struct_enum() {
432        #[derive(Debug, RustcDecodable, PartialEq)]
433        enum Blah {
434            Foo,
435            Bar,
436        }
437
438        #[derive(RustcDecodable)]
439        struct Capture {
440            pub title: Blah,
441            pub year: usize,
442        }
443
444        let re = R::Regex::new(r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)")
445                           .unwrap();
446        let text = "Not my favorite movie: 'Foo' (1941).";
447
448        let val = decode::<Capture>(&re, &text).unwrap();
449
450        assert_eq!(val.title, Blah::Foo);
451        assert_eq!(val.year, 1941);
452    }
453}