Skip to main content

rust_fixed_width/record/
recognizers.rs

1use std::collections::HashMap;
2use spec::RecordSpec;
3use std::io::{Read, Error as IoError};
4use std::fmt::{Display, Error as FmtError, Formatter};
5use std::error::Error as ErrorTrait;
6
7#[derive(Debug)]
8pub enum Error {
9    CouldNotRecognize,
10    Other {
11        repr: Box<::std::error::Error + Send + Sync>
12    }
13}
14
15impl Clone for Error {
16    fn clone(&self) -> Self {
17        match *self {
18            Error::CouldNotRecognize => Error::CouldNotRecognize,
19            Error::Other { .. } => Error::new("")
20        }
21    }
22}
23
24impl Error {
25    pub fn new<E>(error: E) -> Self
26        where E: Into<Box<::std::error::Error + Send + Sync>>
27    {
28        Error::Other { repr: error.into() }
29    }
30
31    pub fn downcast<E: ::std::error::Error + Send + Sync + 'static>(self) -> ::std::result::Result<E, Self> {
32        match self {
33            Error::CouldNotRecognize => Err(Error::CouldNotRecognize),
34            Error::Other { repr } => Ok(*(repr.downcast::<E>().map_err(|e| Error::Other { repr: e })?))
35        }
36    }
37
38    pub fn downcast_ref<E: ::std::error::Error + Send + Sync + 'static>(&self) -> Option<&E> {
39        match *self {
40            Error::CouldNotRecognize => None,
41            Error::Other { ref repr } => repr.downcast_ref::<E>()
42        }
43    }
44}
45
46impl ErrorTrait for Error {
47    fn description(&self) -> &str {
48        match *self {
49            Error::CouldNotRecognize => "Could not recognize as any specific record spec",
50            Error::Other { repr: ref e } => e.description()
51        }
52    }
53
54    fn cause(&self) -> Option<&::std::error::Error> {
55        match *self {
56            Error::CouldNotRecognize => None,
57            Error::Other { repr: ref e } => e.cause()
58        }
59    }
60}
61
62impl Display for Error {
63    fn fmt(&self, f: &mut Formatter) -> ::std::result::Result<(), FmtError> {
64        match *self {
65            Error::CouldNotRecognize => write!(f, "{}", self.description()),
66            Error::Other { repr: ref e } => e.fmt(f),
67        }
68    }
69}
70
71impl From<IoError> for Error {
72    fn from(e: IoError) -> Self {
73        Error::new(e)
74    }
75}
76
77type Result<T> = ::std::result::Result<T, Error>;
78
79pub struct LineBuffer<'a, T: Read + 'a> {
80    reader: &'a mut T,
81    line: &'a mut String
82}
83
84impl<'a, T: Read + 'a> LineBuffer<'a, T> {
85    pub fn new(reader: &'a mut T, line: &'a mut String) -> Self {
86        LineBuffer {
87            reader: reader,
88            line: line
89        }
90    }
91
92    pub fn fill_to(&mut self, size: usize) -> ::std::result::Result<(), IoError> {
93        let length = self.line.len();
94        if length < size {
95            (*self).reader.by_ref().take((size - self.line.len()) as u64).read_to_string(self.line)?;
96        }
97
98        Ok(())
99    }
100
101    pub fn into_inner(self) -> (&'a mut T, &'a mut String) {
102        (self.reader, self.line)
103    }
104
105    pub fn get_line(&mut self) -> &mut String {
106        self.line
107    }
108}
109
110pub trait LineRecordSpecRecognizer {
111    fn recognize_for_line<'a, T: Read + 'a>(&self, buffer: LineBuffer<'a, T>, record_specs: &HashMap<String, RecordSpec>) -> Result<String>;
112}
113
114impl<'a, V> LineRecordSpecRecognizer for &'a V where V: 'a + LineRecordSpecRecognizer {
115    fn recognize_for_line<'b, T: Read + 'b>(&self, buffer: LineBuffer<'b, T>, record_specs: &HashMap<String, RecordSpec>) -> Result<String> {
116        (**self).recognize_for_line(buffer, record_specs)
117    }
118}
119
120pub trait DataRecordSpecRecognizer {
121    fn recognize_for_data(&self, data: &HashMap<String, String>, record_specs: &HashMap<String, RecordSpec>) -> Result<String>;
122}
123
124impl<'a, T> DataRecordSpecRecognizer for &'a T where T: 'a + DataRecordSpecRecognizer {
125    fn recognize_for_data(&self, data: &HashMap<String, String>, record_specs: &HashMap<String, RecordSpec>) -> Result<String> {
126        (**self).recognize_for_data(data, record_specs)
127    }
128}
129
130pub struct IdFieldRecognizer {
131    id_field: String
132}
133
134impl IdFieldRecognizer {
135    pub fn new() -> Self {
136        Self::new_with_field("$id")
137    }
138
139    pub fn new_with_field<T: Into<String>>(id_field: T) -> Self {
140        IdFieldRecognizer { id_field: id_field.into() }
141    }
142}
143
144impl LineRecordSpecRecognizer for IdFieldRecognizer {
145    fn recognize_for_line<'a, T: Read + 'a>(&self, mut buffer: LineBuffer<'a, T>, record_specs: &HashMap<String, RecordSpec>) -> Result<String> {
146        for (name, record_spec) in record_specs.iter() {
147            if let Some(ref field_spec) = record_spec.field_specs.get(&self.id_field) {
148                if let Some(ref default) = field_spec.default {
149                    let field_range = record_spec.field_range(&self.id_field).expect("This should never be None");
150                    buffer.fill_to(field_range.end)?;
151
152                    if buffer.get_line().len() < field_range.end {
153                        continue;
154                    }
155
156                    if &buffer.get_line()[field_range] == default {
157                        return Ok(name.clone());
158                    }
159                }
160            }
161        }
162
163        Err(Error::CouldNotRecognize)
164    }
165}
166
167impl DataRecordSpecRecognizer for IdFieldRecognizer {
168    fn recognize_for_data(&self, data: &HashMap<String, String>, record_specs: &HashMap<String, RecordSpec>) -> Result<String> {
169        for (name, record_spec) in record_specs.iter() {
170            if let Some(ref field_spec) = record_spec.field_specs.get(&self.id_field) {
171                if let Some(ref default) = field_spec.default {
172                    if let Some(string) = data.get(&self.id_field) {
173                        if string == default {
174                            return Ok(name.clone());
175                        }
176                    }
177                }
178            }
179        }
180
181        Err(Error::CouldNotRecognize)
182    }
183}
184
185pub struct NoneRecognizer;
186
187impl LineRecordSpecRecognizer for NoneRecognizer {
188    fn recognize_for_line<'a, T: Read + 'a>(&self, _: LineBuffer<'a, T>, _: &HashMap<String, RecordSpec>) -> Result<String> {
189        Err(Error::CouldNotRecognize)
190    }
191}
192
193impl DataRecordSpecRecognizer for NoneRecognizer {
194    fn recognize_for_data(&self, _: &HashMap<String, String>, _: &HashMap<String, RecordSpec>) -> Result<String> {
195        Err(Error::CouldNotRecognize)
196    }
197}
198
199#[cfg(test)]
200#[macro_use]
201mod test {
202    use super::*;
203    use spec::*;
204    use std::collections::HashMap;
205    use std::io::empty;
206    use padders::PaddingError;
207
208    #[test]
209    fn none_recognizer() {
210        let recognizer = NoneRecognizer;
211        assert_result!(Err(Error::CouldNotRecognize), recognizer.recognize_for_data(&HashMap::new(), &HashMap::new()));
212        assert_result!(Err(Error::CouldNotRecognize), recognizer.recognize_for_line(
213            LineBuffer::new(&mut empty(), &mut String::new()),
214            &HashMap::new()
215        ));
216    }
217
218    #[test]
219    fn id_spec_recognizer() {
220        let specs = SpecBuilder::new()
221            .with_record(
222                "record1",
223                RecordSpecBuilder::new()
224                    .with_field(
225                        "field1",
226                        FieldSpecBuilder::new()
227                            .with_default("foo")
228                            .with_length(3)
229                            .with_padding("dsasd")
230                            .with_padding_direction(PaddingDirection::Left)
231                    )
232                    .with_field(
233                        "field2",
234                        FieldSpecBuilder::new()
235                            .with_length(5)
236                            .with_padding("sdf".to_string())
237                            .with_padding_direction(PaddingDirection::Right)
238                    )
239            )
240            .with_record(
241                "record2",
242                RecordSpecBuilder::new()
243                    .with_field(
244                        "$id",
245                        FieldSpecBuilder::new_string()
246                            .with_default("bar")
247                            .with_length(3)
248                    )
249                    .with_field(
250                        "field2".to_string(),
251                        FieldSpecBuilder::new_string()
252                            .with_length(5)
253                    )
254            ).with_record(
255            "record3",
256            RecordSpecBuilder::new()
257                .with_field(
258                    "field1",
259                    FieldSpecBuilder::new_string()
260                        .with_default("bar")
261                        .with_length(3)
262                )
263                .with_field(
264                    "field2",
265                    FieldSpecBuilder::new_string()
266                        .with_length(5)
267                )
268        )
269            .with_record(
270                "record4",
271                RecordSpecBuilder::new()
272                    .with_field(
273                        "$id",
274                        FieldSpecBuilder::new_string()
275                            .with_default("foo")
276                            .with_length(3)
277                    )
278                    .with_field(
279                        "field2".to_string(),
280                        FieldSpecBuilder::new_string()
281                            .with_length(5)
282                    )
283            )
284            .build()
285            .record_specs
286        ;
287        let recognizer = IdFieldRecognizer::new();
288        let recognizer_with_field = IdFieldRecognizer::new_with_field("field1");
289        let mut data = HashMap::new();
290
291        data.insert("$id".to_string(), "bar".to_string());
292        assert_result!(Ok("record2".to_string()), recognizer.recognize_for_data(&data, &specs));
293        assert_result!(Err(Error::CouldNotRecognize), recognizer_with_field.recognize_for_data(&data, &specs));
294
295        data.insert("$id".to_string(), "foo".to_string());
296        assert_result!(Ok("record4".to_string()), recognizer.recognize_for_data(&data, &specs));
297        assert_result!(Err(Error::CouldNotRecognize), recognizer_with_field.recognize_for_data(&data, &specs));
298
299        data.insert("$id".to_string(), "foobar".to_string());
300        assert_result!(Err(Error::CouldNotRecognize), recognizer.recognize_for_data(&data, &specs));
301        assert_result!(Err(Error::CouldNotRecognize), recognizer_with_field.recognize_for_data(&data, &specs));
302
303        data.insert("field1".to_string(), "bar".to_string());
304        assert_result!(Err(Error::CouldNotRecognize), recognizer.recognize_for_data(&data, &specs));
305        assert_result!(Ok("record3".to_string()), recognizer_with_field.recognize_for_data(&data, &specs));
306        data.remove(&"$id".to_string());
307
308        assert_result!(Err(Error::CouldNotRecognize), recognizer.recognize_for_data(&data, &specs));
309        assert_result!(Ok("record3".to_string()), recognizer_with_field.recognize_for_data(&data, &specs));
310
311        data.insert("field1".to_string(), "foo".to_string());
312        assert_result!(Err(Error::CouldNotRecognize), recognizer.recognize_for_data(&data, &specs));
313        assert_result!(Ok("record1".to_string()), recognizer_with_field.recognize_for_data(&data, &specs));
314
315        data.insert("field1".to_string(), "foobar".to_string());
316        assert_result!(Err(Error::CouldNotRecognize), recognizer.recognize_for_data(&data, &specs));
317        assert_result!(Err(Error::CouldNotRecognize), recognizer_with_field.recognize_for_data(&data, &specs));
318
319        assert_result!(Err(Error::CouldNotRecognize), recognizer.recognize_for_line(LineBuffer::new(&mut "dsfdsfsdfd".as_bytes(), &mut String::new()), &specs));
320        assert_result!(Err(Error::CouldNotRecognize), recognizer_with_field.recognize_for_line(LineBuffer::new(&mut "dsfdsfsdfd".as_bytes(), &mut String::new()), &specs));
321        assert_result!(Err(Error::CouldNotRecognize), recognizer_with_field.recognize_for_line(LineBuffer::new(&mut "ba".as_bytes(), &mut String::new()), &specs));
322        assert_result!(Ok("record2".to_string()), recognizer.recognize_for_line(LineBuffer::new(&mut "barasdasdd".as_bytes(), &mut String::new()), &specs));
323        assert_result!(Ok("record3".to_string()), recognizer_with_field.recognize_for_line(LineBuffer::new(&mut "barasdasdd".as_bytes(), &mut String::new()), &specs));
324        assert_result!(Ok("record4".to_string()), recognizer.recognize_for_line(LineBuffer::new(&mut "foodsfsdfd".as_bytes(), &mut String::new()), &specs));
325        assert_result!(Ok("record1".to_string()), recognizer_with_field.recognize_for_line(LineBuffer::new(&mut "foodsfsdfd".as_bytes(), &mut String::new()), &specs));
326    }
327
328    #[test]
329    fn recognizer_reference() {
330        let recognizer = NoneRecognizer;
331        assert_result!(Err(Error::CouldNotRecognize), DataRecordSpecRecognizer::recognize_for_data(&&recognizer, &HashMap::new(), &HashMap::new()));
332        assert_result!(Err(Error::CouldNotRecognize), LineRecordSpecRecognizer::recognize_for_line(
333            &&recognizer,
334            LineBuffer::new(&mut empty(), &mut String::new()),
335            &HashMap::new()
336        ));
337    }
338
339    #[test]
340    fn line_buffer() {
341        let reader = &mut "dsfdsfsdfd".as_bytes();
342        let mut string = String::new();
343        let mut buffer = LineBuffer::new(reader, &mut string);
344        buffer.fill_to(5).unwrap();
345        buffer.fill_to(5).unwrap();
346        assert_eq!(&mut "dsfds".to_string(), buffer.get_line());
347        buffer.fill_to(6).unwrap();
348        assert_eq!(&mut "dsfdsf".to_string(), buffer.get_line());
349        let (buf, line) = buffer.into_inner();
350        assert_eq!(&mut "dsfdsf".to_string(), line);
351        assert_eq!(&mut "sdfd".as_bytes(), buf);
352    }
353
354    #[test]
355    fn error() {
356        let error = Error::new(PaddingError::PaddingLongerThanOne(23));
357        assert_option!(Some(&PaddingError::PaddingLongerThanOne(23)), error.downcast_ref::<PaddingError>());
358        assert_option!(Some(&PaddingError::PaddingLongerThanOne(23)), error.downcast_ref::<PaddingError>());
359        match error.downcast::<PaddingError>() {
360            Ok(PaddingError::PaddingLongerThanOne(23)) => (),
361            e => panic!("bad result returned {:?}", e)
362        }
363    }
364}