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}