From b896fd021e252f6ee988cb79f647a197ec55a3ed Mon Sep 17 00:00:00 2001 From: imbolc Date: Tue, 19 Sep 2023 10:30:32 +0600 Subject: [PATCH 1/4] clippy --fix --- src/columns.rs | 4 ++-- src/error.rs | 2 +- src/lib.rs | 6 +++--- src/test.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/columns.rs b/src/columns.rs index 8bfd152..d9c0fac 100644 --- a/src/columns.rs +++ b/src/columns.rs @@ -44,7 +44,7 @@ impl<'a> BytesColumns<'a> { pub fn new(line: &'a [u8], cols: &'a [usize]) -> BytesColumns<'a> { BytesColumns { pos: 0, - line: line, + line, iter: cols.iter(), } } @@ -87,7 +87,7 @@ impl<'a> Columns<'a> { pub fn new(line: &'a str, cols: &'a [usize]) -> Columns<'a> { Columns { pos: 0, - line: line, + line, iter: cols.iter(), } } diff --git a/src/error.rs b/src/error.rs index cc3c08a..f480d1b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -55,7 +55,7 @@ impl ::std::error::Error for Error { } } - fn cause(&self) -> Option<&::std::error::Error> { + fn cause(&self) -> Option<&dyn (::std::error::Error)> { match *self { Error::Io(ref err) => Some(err), _ => None, diff --git a/src/lib.rs b/src/lib.rs index ca01037..105404f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,7 +66,7 @@ use rustc_serialize::Decodable; #[cfg(test)] mod test; -const UTF8_BOM: &'static [u8] = b"\xef\xbb\xbf"; +const UTF8_BOM: &[u8] = b"\xef\xbb\xbf"; /// Csv reader /// @@ -122,7 +122,7 @@ impl Csv { let result = try_consume_utf8_bom(&mut reader); Csv { - reader: reader, + reader, delimiter: b',', has_header: false, headers: None, @@ -225,7 +225,7 @@ impl Iterator for Csv { self.current_line += 1; Some(Ok(Row { line: buf, - cols: cols, + cols, })) } Err(e) => { diff --git a/src/test.rs b/src/test.rs index df88f26..862e0d2 100644 --- a/src/test.rs +++ b/src/test.rs @@ -160,7 +160,7 @@ decodes_to!(decode_int, "1", (usize,), vec![(1usize,)]); decodes_to!(decode_many_int, "1,2", (usize, i16), vec![(1usize, 2i16)]); decodes_to!(decode_float, "1,1.0,1.5", (f64, f64, f64), vec![(1f64, 1.0, 1.5)]); -decodes_to!(decode_char, "a", (char), vec![('a')]); +decodes_to!(decode_char, "a", char, vec![('a')]); decodes_to!(decode_str, "abc", (String,), vec![("abc".into(),)]); decodes_to!(decode_opt_int, "a", (Option,), vec![(None,)]); From b6a0176def12fe69357003f620af968d1d905299 Mon Sep 17 00:00:00 2001 From: imbolc Date: Tue, 19 Sep 2023 10:37:21 +0600 Subject: [PATCH 2/4] try! -> ? --- src/columns.rs | 194 ++++++++++++++++++++++++++++++++++--------------- src/lib.rs | 116 ++++++++++++++++------------- 2 files changed, 204 insertions(+), 106 deletions(-) diff --git a/src/columns.rs b/src/columns.rs index d9c0fac..1687255 100644 --- a/src/columns.rs +++ b/src/columns.rs @@ -1,12 +1,12 @@ //! Column management module -//! +//! //! Provides convenient or fast column conversions //! Deserialize columns into a `Decodable` type -use std::str::FromStr; +use error::{Error, Result}; use rustc_serialize as serialize; -use error::{Result, Error}; use std::slice::Iter; +use std::str::FromStr; /// Iterator over bytes slice of columns pub struct BytesColumns<'a> { @@ -22,14 +22,17 @@ impl<'a> Iterator for BytesColumns<'a> { self.iter.next().map(|p| { let s = &self.line[self.pos..*p]; self.pos = *p + 1; - if s.starts_with(&[b'\"']) { &s[1..s.len() - 1] } else { s } + if s.starts_with(&[b'\"']) { + &s[1..s.len() - 1] + } else { + s + } }) } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } - } impl<'a> ExactSizeIterator for BytesColumns<'a> { @@ -39,7 +42,6 @@ impl<'a> ExactSizeIterator for BytesColumns<'a> { } impl<'a> BytesColumns<'a> { - /// Creates a new BytesColumns iterator pub fn new(line: &'a [u8], cols: &'a [usize]) -> BytesColumns<'a> { BytesColumns { @@ -48,7 +50,6 @@ impl<'a> BytesColumns<'a> { iter: cols.iter(), } } - } /// &str iterator on columns @@ -65,14 +66,17 @@ impl<'a> Iterator for Columns<'a> { self.iter.next().map(|p| { let s = &self.line[self.pos..*p]; self.pos = *p + 1; - if s.starts_with('\"') { &s[1..s.len() - 1] } else { s } + if s.starts_with('\"') { + &s[1..s.len() - 1] + } else { + s + } }) } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } - } impl<'a> ExactSizeIterator for Columns<'a> { @@ -82,7 +86,6 @@ impl<'a> ExactSizeIterator for Columns<'a> { } impl<'a> Columns<'a> { - /// Creates a new Columns iterator pub fn new(line: &'a str, cols: &'a [usize]) -> Columns<'a> { Columns { @@ -95,25 +98,35 @@ impl<'a> Columns<'a> { fn peek(&self) -> Option<&'a str> { self.iter.clone().next().map(|p| { let s = &self.line[self.pos..*p]; - if s.starts_with('\"') { &s[1..s.len() - 1] } else { s } + if s.starts_with('\"') { + &s[1..s.len() - 1] + } else { + s + } }) } fn next_str(&mut self) -> Result - where T: FromStr + ::std::fmt::Debug, - T::Err: ::std::fmt::Debug + where + T: FromStr + ::std::fmt::Debug, + T::Err: ::std::fmt::Debug, { - self.next().ok_or(Error::EOL).and_then(|col| - FromStr::from_str(col).map_err(|e| - Error::Decode(format!("Failed converting {}th column (\'{}\'):\n\t{:?}", - self.len(), col, e)))) + self.next().ok_or(Error::EOL).and_then(|col| { + FromStr::from_str(col).map_err(|e| { + Error::Decode(format!( + "Failed converting {}th column (\'{}\'):\n\t{:?}", + self.len(), + col, + e + )) + }) + }) } /// Deserializes a Columns iterator into any Decodable type pub fn decode(&mut self) -> Result { serialize::Decodable::decode(self) } - } impl<'a> serialize::Decoder for Columns<'a> { @@ -122,86 +135,143 @@ impl<'a> serialize::Decoder for Columns<'a> { fn error(&mut self, err: &str) -> Error { Error::Decode(err.into()) } - fn read_nil(&mut self) -> Result<()> { unimplemented!() } - fn read_usize(&mut self) -> Result { self.next_str() } - fn read_u64(&mut self) -> Result { self.next_str() } - fn read_u32(&mut self) -> Result { self.next_str() } - fn read_u16(&mut self) -> Result { self.next_str() } - fn read_u8(&mut self) -> Result { self.next_str() } - fn read_isize(&mut self) -> Result { self.next_str() } - fn read_i64(&mut self) -> Result { self.next_str() } - fn read_i32(&mut self) -> Result { self.next_str() } - fn read_i16(&mut self) -> Result { self.next_str() } - fn read_i8(&mut self) -> Result { self.next_str() } - fn read_bool(&mut self) -> Result { self.next_str() } - fn read_f64(&mut self) -> Result { self.next_str() } - fn read_f32(&mut self) -> Result { self.next_str() } + fn read_nil(&mut self) -> Result<()> { + unimplemented!() + } + fn read_usize(&mut self) -> Result { + self.next_str() + } + fn read_u64(&mut self) -> Result { + self.next_str() + } + fn read_u32(&mut self) -> Result { + self.next_str() + } + fn read_u16(&mut self) -> Result { + self.next_str() + } + fn read_u8(&mut self) -> Result { + self.next_str() + } + fn read_isize(&mut self) -> Result { + self.next_str() + } + fn read_i64(&mut self) -> Result { + self.next_str() + } + fn read_i32(&mut self) -> Result { + self.next_str() + } + fn read_i16(&mut self) -> Result { + self.next_str() + } + fn read_i8(&mut self) -> Result { + self.next_str() + } + fn read_bool(&mut self) -> Result { + self.next_str() + } + fn read_f64(&mut self) -> Result { + self.next_str() + } + fn read_f32(&mut self) -> Result { + self.next_str() + } fn read_char(&mut self) -> Result { - let col = try!(self.next().ok_or(Error::EOL)); + let col = self.next().ok_or(Error::EOL)?; if col.len() != 1 { return Err(Error::Decode(format!( - "Expected a single char, found {} chars", col.len()))); + "Expected a single char, found {} chars", + col.len() + ))); } Ok(col.chars().next().unwrap()) } - fn read_str(&mut self) -> Result { self.next_str() } + fn read_str(&mut self) -> Result { + self.next_str() + } fn read_enum(&mut self, _: &str, f: F) -> Result - where F: FnOnce(&mut Columns<'a>) -> Result { + where + F: FnOnce(&mut Columns<'a>) -> Result, + { f(self) } fn read_enum_variant(&mut self, names: &[&str], mut f: F) -> Result - where F: FnMut(&mut Columns<'a>, usize) -> Result { - let i = try!(self + where + F: FnMut(&mut Columns<'a>, usize) -> Result, + { + let i = self .peek() .and_then(|name| names.iter().position(|&n| n == name)) .ok_or(Error::Decode(format!( - "Could not load value into any variant in {:?}", names)))); + "Could not load value into any variant in {:?}", + names + )))?; let _ = self.next(); f(self, i) } fn read_enum_variant_arg(&mut self, _: usize, f: F) -> Result - where F: FnOnce(&mut Columns<'a>) -> Result { + where + F: FnOnce(&mut Columns<'a>) -> Result, + { f(self) } fn read_enum_struct_variant(&mut self, names: &[&str], f: F) -> Result - where F: FnMut(&mut Columns<'a>, usize) -> Result { + where + F: FnMut(&mut Columns<'a>, usize) -> Result, + { self.read_enum_variant(names, f) } - fn read_enum_struct_variant_field(&mut self, _: &str, - f_idx: usize, f: F) -> Result - where F: FnOnce(&mut Columns<'a>) -> Result { + fn read_enum_struct_variant_field(&mut self, _: &str, f_idx: usize, f: F) -> Result + where + F: FnOnce(&mut Columns<'a>) -> Result, + { self.read_enum_variant_arg(f_idx, f) } fn read_struct(&mut self, _: &str, _: usize, f: F) -> Result - where F: FnOnce(&mut Columns<'a>) -> Result { + where + F: FnOnce(&mut Columns<'a>) -> Result, + { f(self) } fn read_struct_field(&mut self, _: &str, _: usize, f: F) -> Result - where F: FnOnce(&mut Columns<'a>) -> Result { + where + F: FnOnce(&mut Columns<'a>) -> Result, + { f(self) } fn read_tuple(&mut self, _: usize, f: F) -> Result - where F: FnOnce(&mut Columns<'a>) -> Result { + where + F: FnOnce(&mut Columns<'a>) -> Result, + { f(self) } fn read_tuple_arg(&mut self, _: usize, f: F) -> Result - where F: FnOnce(&mut Columns<'a>) -> Result { + where + F: FnOnce(&mut Columns<'a>) -> Result, + { f(self) } fn read_tuple_struct(&mut self, _: &str, _: usize, _: F) -> Result - where F: FnOnce(&mut Columns<'a>) -> Result { + where + F: FnOnce(&mut Columns<'a>) -> Result, + { unimplemented!() } fn read_tuple_struct_arg(&mut self, _: usize, _: F) -> Result - where F: FnOnce(&mut Columns<'a>) -> Result { + where + F: FnOnce(&mut Columns<'a>) -> Result, + { unimplemented!() } fn read_option(&mut self, mut f: F) -> Result - where F: FnMut(&mut Columns<'a>, bool) -> Result { - let col = try!(self.peek().ok_or(Error::EOL)); + where + F: FnMut(&mut Columns<'a>, bool) -> Result, + { + let col = self.peek().ok_or(Error::EOL)?; if col.is_empty() { let _ = self.iter.next(); f(self, false) @@ -211,24 +281,34 @@ impl<'a> serialize::Decoder for Columns<'a> { } fn read_seq(&mut self, f: F) -> Result - where F: FnOnce(&mut Columns<'a>, usize) -> Result { + where + F: FnOnce(&mut Columns<'a>, usize) -> Result, + { let len = self.iter.clone().count(); f(self, len) } fn read_seq_elt(&mut self, _: usize, f: F) -> Result - where F: FnOnce(&mut Columns<'a>) -> Result { + where + F: FnOnce(&mut Columns<'a>) -> Result, + { f(self) } fn read_map(&mut self, _: F) -> Result - where F: FnOnce(&mut Columns<'a>, usize) -> Result { + where + F: FnOnce(&mut Columns<'a>, usize) -> Result, + { unimplemented!() } fn read_map_elt_key(&mut self, _: usize, _: F) -> Result - where F: FnOnce(&mut Columns<'a>) -> Result { + where + F: FnOnce(&mut Columns<'a>) -> Result, + { unimplemented!() } fn read_map_elt_val(&mut self, _: usize, _: F) -> Result - where F: FnOnce(&mut Columns<'a>) -> Result { + where + F: FnOnce(&mut Columns<'a>) -> Result, + { unimplemented!() } } diff --git a/src/lib.rs b/src/lib.rs index 105404f..6cf29fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,12 @@ //! Quick Csv reader which performs **very** well. -//! +//! //! ## Example -//! +//! //! First, create a `Csv` from a `BufRead` reader, a file or a string -//! +//! //! ```rust //! extern crate quick_csv; -//! +//! //! fn main() { //! let data = "a,b\r\nc,d\r\ne,f"; //! let csv = quick_csv::Csv::from_string(data); @@ -20,30 +20,30 @@ //! } //! } //! ``` -//! +//! //! `Row` is on the other hand provides 3 methods to access csv columns: -//! - `columns`: +//! - `columns`: //! - iterator over columns. //! - Iterator item is a `&str`, which means you only have to `parse()` it to the needed type and you're done -//! +//! //! ```rust //! # let row = quick_csv::Csv::from_string("a,b,c,d,e,38,f").next().unwrap().unwrap(); //! let mut cols = row.columns().expect("cannot convert to utf8"); //! let fifth = cols.nth(5).unwrap().parse::().unwrap(); //! println!("Doubled fifth column: {}", fifth * 2.0); //! ``` -//! +//! //! - `decode`: //! - deserialize into you `Decodable` struct, a-la rust-csv. //! - most convenient way to deal with your csv data -//! +//! //! ```rust //! let row = quick_csv::Csv::from_string("a,b,54").next().unwrap().unwrap(); //! if let Ok((col1, col2, col3)) = row.decode::<(String, u64, f64)>() { //! println!("col1: '{}', col2: {}, col3: {}", col1, col2, col3); //! } -//! ``` -//! +//! ``` +//! //! - `bytes_columns`: //! - similar to `columns` but columns are of type `&[u8]`, which means you may want to convert it to &str first //! - performance gain compared to `columns` is minimal, use it only if you *really* need to as it is less convenient @@ -55,7 +55,7 @@ extern crate rustc_serialize; pub mod columns; pub mod error; -use self::columns::{Columns, BytesColumns}; +use self::columns::{BytesColumns, Columns}; use std::fs::File; use std::io::{self, BufRead, BufReader}; use std::iter::Iterator; @@ -64,12 +64,13 @@ use std::path::Path; use error::{Error, Result}; use rustc_serialize::Decodable; -#[cfg(test)] mod test; +#[cfg(test)] +mod test; const UTF8_BOM: &[u8] = b"\xef\xbb\xbf"; /// Csv reader -/// +/// /// Iterates over the rows of the csv /// /// # Example @@ -77,7 +78,7 @@ const UTF8_BOM: &[u8] = b"\xef\xbb\xbf"; /// ```rust /// let csv = quick_csv::Csv::from_file("./examples/data/bench.csv").unwrap(); /// for row in csv.into_iter() { -/// let row = row.unwrap(); // unwrap result, panic if not utf8 +/// let row = row.unwrap(); // unwrap result, panic if not utf8 /// { /// // either use columns iterator directly (Item = &str) /// if let Ok(mut columns) = row.columns() { @@ -114,9 +115,8 @@ pub struct Csv { } impl Csv { - /// Creates a Csv from a generic BufReader - /// + /// /// Note: default delimiter = ',' pub fn from_reader(mut reader: B) -> Csv { let result = try_consume_utf8_bom(&mut reader); @@ -152,12 +152,12 @@ impl Csv { self } - /// gets first row as Vec + /// gets first row as Vec pub fn headers(&mut self) -> Vec { if let Some(ref h) = self.headers { return h.clone(); } - if self.has_header { + if self.has_header { if let Some(r) = self.next() { if let Ok(r) = r { let h = r.decode().ok().unwrap_or_else(Vec::new); @@ -180,14 +180,12 @@ impl Csv { pub fn current_line(&self) -> usize { self.current_line } - } impl Csv> { /// Creates a csv from a file path - pub fn from_file>(path: P) -> Result>> - { - let reader = BufReader::new(try!(File::open(path))); + pub fn from_file>(path: P) -> Result>> { + let reader = BufReader::new(File::open(path)?); Ok(Csv::from_reader(reader)) } } @@ -203,7 +201,9 @@ impl<'a> Csv<&'a [u8]> { impl Iterator for Csv { type Item = Result; fn next(&mut self) -> Option> { - if self.exit { return None; } + if self.exit { + return None; + } let mut buf = Vec::new(); let mut cols = self.len.map_or_else(Vec::new, Vec::with_capacity); match read_line(&mut self.reader, &mut buf, self.delimiter, &mut cols) { @@ -223,15 +223,12 @@ impl Iterator for Csv { self.len = Some(c); } self.current_line += 1; - Some(Ok(Row { - line: buf, - cols, - })) + Some(Ok(Row { line: buf, cols })) } Err(e) => { self.exit = true; Some(Err(e)) - }, + } } } } @@ -245,12 +242,13 @@ pub struct Row { } impl Row { - /// Gets an iterator over columns pub fn columns(&self) -> Result { match ::std::str::from_utf8(&self.line) { - Err(_) => Err(Error::Io(io::Error::new(io::ErrorKind::InvalidData, - "stream did not contain valid UTF-8"))), + Err(_) => Err(Error::Io(io::Error::new( + io::ErrorKind::InvalidData, + "stream did not contain valid UTF-8", + ))), Ok(s) => Ok(Columns::new(s, &self.cols)), } } @@ -262,7 +260,7 @@ impl Row { /// Decode row into custom decodable type pub fn decode(&self) -> Result { - let mut columns = try!(self.columns()); + let mut columns = self.columns()?; Decodable::decode(&mut columns) } @@ -275,7 +273,6 @@ impl Row { pub fn is_empty(&self) -> bool { self.cols.is_empty() } - } /// Consumes bytes as long as they are within quotes @@ -297,26 +294,29 @@ macro_rules! consume_quote { $buf.extend_from_slice(&$available[$start..i]); $start = i + 1; $quote_count += 1; - }, + } None | Some((_, &b'\r')) | Some((_, &b'\n')) => break, Some((_, d)) if *d == $delimiter => break, Some((_, _)) => return Err(Error::UnescapedQuote), } - }, + } None => { $in_quote = true; break; - }, + } _ => (), } } - } + }; } /// Reads an entire line into memory -fn read_line(r: &mut R, buf: &mut Vec, - delimiter: u8, cols: &mut Vec) -> Result -{ +fn read_line( + r: &mut R, + buf: &mut Vec, + delimiter: u8, + cols: &mut Vec, +) -> Result { let mut read = 0; let mut in_quote = false; let mut done = false; @@ -335,7 +335,15 @@ fn read_line(r: &mut R, buf: &mut Vec, // previous buffer was exhausted without exiting from quotes if in_quote { - consume_quote!(bytes, delimiter, in_quote, start, buf, available, quote_count); + consume_quote!( + bytes, + delimiter, + in_quote, + start, + buf, + available, + quote_count + ); } // use a simple loop instead of for loop to allow nested loop @@ -344,25 +352,35 @@ fn read_line(r: &mut R, buf: &mut Vec, match bytes.next() { Some((i, &b'\"')) => { if i == 0 || available[i - 1] == delimiter { - consume_quote!(bytes, delimiter, in_quote, start, buf, available, quote_count); + consume_quote!( + bytes, + delimiter, + in_quote, + start, + buf, + available, + quote_count + ); } else { return Err(Error::UnexpextedQuote); } - }, + } Some((i, &b'\n')) => { done = true; used = i + 1; buf.extend_from_slice(&available[start..i]); break; - }, + } Some((i, &d)) => { - if d == delimiter { cols.push(read + i - quote_count); } - }, + if d == delimiter { + cols.push(read + i - quote_count); + } + } None => { used = available.len(); buf.extend_from_slice(&available[start..used]); break; - }, + } } } used @@ -374,7 +392,7 @@ fn read_line(r: &mut R, buf: &mut Vec, } fn try_consume_utf8_bom(reader: &mut B) -> Result<()> { - if try!(reader.fill_buf()).starts_with(UTF8_BOM) { + if reader.fill_buf()?.starts_with(UTF8_BOM) { reader.consume(UTF8_BOM.len()); } From af800dcb6e64d663f6b52288271048ccf8414213 Mon Sep 17 00:00:00 2001 From: imbolc Date: Tue, 19 Sep 2023 10:43:18 +0600 Subject: [PATCH 3/4] More clippy --- src/lib.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6cf29fe..8049ece 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -158,12 +158,10 @@ impl Csv { return h.clone(); } if self.has_header { - if let Some(r) = self.next() { - if let Ok(r) = r { - let h = r.decode().ok().unwrap_or_else(Vec::new); - self.headers = Some(h.clone()); - return h; - } + if let Some(Ok(r)) = self.next() { + let h: Vec<_> = r.decode().ok().unwrap_or_default(); + self.headers = Some(h.clone()); + return h; } } Vec::new() From 7a410c6f8d015b63df8dc0397bc1068211cddd62 Mon Sep 17 00:00:00 2001 From: imbolc Date: Tue, 19 Sep 2023 10:47:33 +0600 Subject: [PATCH 4/4] Fmt --- benches/bench.rs | 11 +- src/error.rs | 20 ++-- src/test.rs | 265 ++++++++++++++++++++++++++++++++--------------- 3 files changed, 203 insertions(+), 93 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 53c7c35..b99951f 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -4,16 +4,17 @@ extern crate quick_csv as csv; extern crate rustc_serialize; extern crate test; +use csv::Csv; use std::fmt::{Debug, Display}; use std::fs; use std::io::Read; use test::Bencher; -use csv::Csv; static CSV_DATA: &'static str = "./examples/data/bench.csv"; -fn ordie(r: Result) -> T { - r.or_else(|e: E| -> Result { panic!(format!("{:?}", e)) }).unwrap() +fn ordie(r: Result) -> T { + r.or_else(|e: E| -> Result { panic!(format!("{:?}", e)) }) + .unwrap() } fn file_to_mem(fp: &str) -> Vec { @@ -31,7 +32,7 @@ fn str_records(b: &mut Bencher) { let dec = Csv::from_reader(&*data); for row in dec.into_iter() { for c in row.unwrap().columns() { - let _ = c; + let _ = c; } } }) @@ -45,7 +46,7 @@ fn bytes_records(b: &mut Bencher) { let dec = Csv::from_reader(&*data); for row in dec.into_iter() { for c in row.unwrap().bytes_columns() { - let _ = c; + let _ = c; } } }) diff --git a/src/error.rs b/src/error.rs index f480d1b..e12d7df 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,5 @@ //! Error management module -//! +//! //! Provides all csv error conversion and description //! Also provides `Result` as a alias of `Result<_, Error> @@ -36,8 +36,13 @@ impl fmt::Display for Error { Error::Io(ref err) => write!(f, "{}", err), Error::EOL => write!(f, "Trying to access column but found End Of Line"), Error::UnescapedQuote => write!(f, "A CSV column has an unescaped quote"), - Error::UnexpextedQuote => write!(f, "A CSV column has a quote but the entire column value is not quoted"), - Error::ColumnMismatch(exp, cur) => write!(f, "Expectiong {} columns, found {}", exp, cur), + Error::UnexpextedQuote => write!( + f, + "A CSV column has a quote but the entire column value is not quoted" + ), + Error::ColumnMismatch(exp, cur) => { + write!(f, "Expectiong {} columns, found {}", exp, cur) + } } } } @@ -50,7 +55,9 @@ impl ::std::error::Error for Error { Error::Io(..) => "CSV IO error", Error::EOL => "Trying to access column but found End Of Line", Error::UnescapedQuote => "A CSV column has an unescaped quote", - Error::UnexpextedQuote => "A CSV column has a quote but the entire column value is not quoted", + Error::UnexpextedQuote => { + "A CSV column has a quote but the entire column value is not quoted" + } Error::ColumnMismatch(..) => "Current column count mismatch with previous rows", } } @@ -64,6 +71,7 @@ impl ::std::error::Error for Error { } impl From<::std::io::Error> for Error { - fn from(err: ::std::io::Error) -> Error { Error::Io(err) } + fn from(err: ::std::io::Error) -> Error { + Error::Io(err) + } } - diff --git a/src/test.rs b/src/test.rs index 862e0d2..fd3ead7 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,74 +1,89 @@ -use Csv; use std::io::{self, Read, Seek}; +use Csv; fn assert_svec_eq(got: Vec>, expected: Vec>) - where S: AsRef, T: AsRef { - let got: Vec> = - got.iter().map(|row| { - row.iter().map(|f| f.as_ref()).collect() - }).collect(); - let expected: Vec> = - expected.iter().map(|row| { - row.iter().map(|f| f.as_ref()).collect() - }).collect(); +where + S: AsRef, + T: AsRef, +{ + let got: Vec> = got + .iter() + .map(|row| row.iter().map(|f| f.as_ref()).collect()) + .collect(); + let expected: Vec> = expected + .iter() + .map(|row| row.iter().map(|f| f.as_ref()).collect()) + .collect(); println!("got len: {}, expected len: {}", got.len(), expected.len()); - println!("got lengths: {:?}", - got.iter().map(|row: &Vec<&str>| row.len()) - .collect::>()); - println!("expected lengths: {:?}", - expected.iter().map(|row: &Vec<&str>| row.len()) - .collect::>()); + println!( + "got lengths: {:?}", + got.iter() + .map(|row: &Vec<&str>| row.len()) + .collect::>() + ); + println!( + "expected lengths: {:?}", + expected + .iter() + .map(|row: &Vec<&str>| row.len()) + .collect::>() + ); assert_eq!(got, expected); } macro_rules! parses_to { - ($name:ident, $csv:expr, $vec:expr) => ( + ($name:ident, $csv:expr, $vec:expr) => { parses_to!($name, $csv, $vec, |rdr| rdr); - ); - ($name:ident, $csv:expr, $vec:expr, $config:expr) => ( + }; + ($name:ident, $csv:expr, $vec:expr, $config:expr) => { #[test] fn $name() { let mut rdr = Csv::from_string($csv); rdr = $config(rdr); - let rows = rdr.map(|r| r.and_then(|r| r.decode()).unwrap()) - .collect::>>(); + let rows = rdr + .map(|r| r.and_then(|r| r.decode()).unwrap()) + .collect::>>(); assert_svec_eq::(rows, $vec); } - ); + }; } macro_rules! fail_parses_to { - ($name:ident, $csv:expr, $vec:expr) => ( + ($name:ident, $csv:expr, $vec:expr) => { fail_parses_to!($name, $csv, $vec, |rdr| rdr); - ); - ($name:ident, $csv:expr, $vec:expr, $config:expr) => ( + }; + ($name:ident, $csv:expr, $vec:expr, $config:expr) => { #[test] #[should_panic] fn $name() { let mut rdr = Csv::from_string($csv); rdr = $config(rdr); - let rows = rdr.map(|r| r.and_then(|r| r.decode()).unwrap()) - .collect::>>(); + let rows = rdr + .map(|r| r.and_then(|r| r.decode()).unwrap()) + .collect::>>(); assert_svec_eq::(rows, $vec); } - ); + }; } macro_rules! decodes_to { - ($name:ident, $csv:expr, $ty:ty, $vec:expr) => ( + ($name:ident, $csv:expr, $ty:ty, $vec:expr) => { decodes_to!($name, $csv, $ty, $vec, false); - ); - ($name:ident, $csv:expr, $ty:ty, $vec:expr, $headers:expr) => ( + }; + ($name:ident, $csv:expr, $ty:ty, $vec:expr, $headers:expr) => { #[test] fn $name() { let mut rdr = Csv::from_string($csv); - if $headers { rdr.next(); } - let rows = rdr.map(|r| r.unwrap().decode().unwrap()) - .collect::>(); + if $headers { + rdr.next(); + } + let rows = rdr + .map(|r| r.unwrap().decode().unwrap()) + .collect::>(); assert_eq!(rows, $vec); } - ); + }; } parses_to!(one_row_one_field, "a", vec![vec!["a"]]); @@ -76,37 +91,78 @@ parses_to!(one_row_many_fields, "a,b,c", vec![vec!["a", "b", "c"]]); parses_to!(one_row_trailing_comma, "a,b,", vec![vec!["a", "b", ""]]); parses_to!(one_row_one_field_lf, "a\n", vec![vec!["a"]]); parses_to!(one_row_many_fields_lf, "a,b,c\n", vec![vec!["a", "b", "c"]]); -parses_to!(one_row_trailing_comma_lf, "a,b,\n", vec![vec!["a", "b", ""]]); +parses_to!( + one_row_trailing_comma_lf, + "a,b,\n", + vec![vec!["a", "b", ""]] +); parses_to!(one_row_one_field_crlf, "a\r\n", vec![vec!["a"]]); -parses_to!(one_row_many_fields_crlf, "a,b,c\r\n", vec![vec!["a", "b", "c"]]); -parses_to!(one_row_trailing_comma_crlf, "a,b,\r\n", vec![vec!["a", "b", ""]]); +parses_to!( + one_row_many_fields_crlf, + "a,b,c\r\n", + vec![vec!["a", "b", "c"]] +); +parses_to!( + one_row_trailing_comma_crlf, + "a,b,\r\n", + vec![vec!["a", "b", ""]] +); parses_to!(one_row_one_field_cr, "a\r", vec![vec!["a"]]); parses_to!(one_row_many_fields_cr, "a,b,c\r", vec![vec!["a", "b", "c"]]); -parses_to!(one_row_trailing_comma_cr, "a,b,\r", vec![vec!["a", "b", ""]]); +parses_to!( + one_row_trailing_comma_cr, + "a,b,\r", + vec![vec!["a", "b", ""]] +); parses_to!(many_rows_one_field, "a\nb", vec![vec!["a"], vec!["b"]]); -parses_to!(many_rows_many_fields, - "a,b,c\nx,y,z", vec![vec!["a", "b", "c"], vec!["x", "y", "z"]]); -parses_to!(many_rows_trailing_comma, - "a,b,\nx,y,", vec![vec!["a", "b", ""], vec!["x", "y", ""]]); +parses_to!( + many_rows_many_fields, + "a,b,c\nx,y,z", + vec![vec!["a", "b", "c"], vec!["x", "y", "z"]] +); +parses_to!( + many_rows_trailing_comma, + "a,b,\nx,y,", + vec![vec!["a", "b", ""], vec!["x", "y", ""]] +); parses_to!(many_rows_one_field_lf, "a\nb\n", vec![vec!["a"], vec!["b"]]); -parses_to!(many_rows_many_fields_lf, - "a,b,c\nx,y,z\n", vec![vec!["a", "b", "c"], vec!["x", "y", "z"]]); -parses_to!(many_rows_trailing_comma_lf, - "a,b,\nx,y,\n", vec![vec!["a", "b", ""], vec!["x", "y", ""]]); -parses_to!(many_rows_one_field_crlf, "a\r\nb\r\n", vec![vec!["a"], vec!["b"]]); -parses_to!(many_rows_many_fields_crlf, - "a,b,c\r\nx,y,z\r\n", - vec![vec!["a", "b", "c"], vec!["x", "y", "z"]]); -parses_to!(many_rows_trailing_comma_crlf, - "a,b,\r\nx,y,\r\n", vec![vec!["a", "b", ""], vec!["x", "y", ""]]); +parses_to!( + many_rows_many_fields_lf, + "a,b,c\nx,y,z\n", + vec![vec!["a", "b", "c"], vec!["x", "y", "z"]] +); +parses_to!( + many_rows_trailing_comma_lf, + "a,b,\nx,y,\n", + vec![vec!["a", "b", ""], vec!["x", "y", ""]] +); +parses_to!( + many_rows_one_field_crlf, + "a\r\nb\r\n", + vec![vec!["a"], vec!["b"]] +); +parses_to!( + many_rows_many_fields_crlf, + "a,b,c\r\nx,y,z\r\n", + vec![vec!["a", "b", "c"], vec!["x", "y", "z"]] +); +parses_to!( + many_rows_trailing_comma_crlf, + "a,b,\r\nx,y,\r\n", + vec![vec!["a", "b", ""], vec!["x", "y", ""]] +); parses_to!(empty_string_no_headers, "", vec![]); -parses_to!(empty_string_headers, "", vec![], - |rdr: Csv<_>| rdr.has_header(true)); -parses_to!(empty_lines, "\n\n\n\n", vec![vec![""], vec![""], vec![""], vec![""]]); +parses_to!(empty_string_headers, "", vec![], |rdr: Csv<_>| rdr + .has_header(true)); +parses_to!( + empty_lines, + "\n\n\n\n", + vec![vec![""], vec![""], vec![""], vec![""]] +); -// The following tests are done in xml-rs and left commented because +// The following tests are done in xml-rs and left commented because // the expected behavior is not clear // // parses_to!(trailing_lines_no_record, @@ -134,32 +190,60 @@ parses_to!(quote_lf, "\"\"\n", vec![vec![""]]); parses_to!(quote_space, "\" \"", vec![vec![" "]]); parses_to!(quote_inner_space, "\" a \"", vec![vec![" a "]]); fail_parses_to!(quote_outer_space, " \"a\" ", vec![vec![" \"a\" "]]); -parses_to!(quote_inner_quote, "a,b,\"c\"\"d\",e", vec![vec!["a", "b", "c\"d", "e"]]); -fail_parses_to!(inner_quote_without_quoted_column, "a,b,c\"\"d,e", vec![vec!["a", "b", "c\"d", "e"]]); +parses_to!( + quote_inner_quote, + "a,b,\"c\"\"d\",e", + vec![vec!["a", "b", "c\"d", "e"]] +); +fail_parses_to!( + inner_quote_without_quoted_column, + "a,b,c\"\"d,e", + vec![vec!["a", "b", "c\"d", "e"]] +); -parses_to!(delimiter_tabs, "a\tb", vec![vec!["a", "b"]], - |rdr: Csv<_>| rdr.delimiter(b'\t')); -parses_to!(delimiter_weird, "azb", vec![vec!["a", "b"]], - |rdr: Csv<_>| rdr.delimiter(b'z')); +parses_to!(delimiter_tabs, "a\tb", vec![vec!["a", "b"]], |rdr: Csv< + _, +>| rdr + .delimiter(b'\t')); +parses_to!(delimiter_weird, "azb", vec![vec!["a", "b"]], |rdr: Csv< + _, +>| rdr + .delimiter(b'z')); -parses_to!(headers_absent, "a\nb", vec![vec!["b"]], - |rdr: Csv<_>| rdr.has_header(true)); - -parses_to!(flexible_rows, "a\nx,y", vec![vec!["a"], vec!["x", "y"]], - |rdr: Csv<_>| rdr.flexible(true)); -parses_to!(flexible_rows2, "a,b\nx", vec![vec!["a", "b"], vec!["x"]], - |rdr: Csv<_>| rdr.flexible(true)); +parses_to!(headers_absent, "a\nb", vec![vec!["b"]], |rdr: Csv<_>| rdr + .has_header(true)); + +parses_to!( + flexible_rows, + "a\nx,y", + vec![vec!["a"], vec!["x", "y"]], + |rdr: Csv<_>| rdr.flexible(true) +); +parses_to!( + flexible_rows2, + "a,b\nx", + vec![vec!["a", "b"], vec!["x"]], + |rdr: Csv<_>| rdr.flexible(true) +); fail_parses_to!(nonflexible, "a\nx,y", vec![]); fail_parses_to!(nonflexible2, "a,b\nx", vec![]); #[derive(Debug, RustcDecodable, RustcEncodable, PartialEq, Eq)] -enum Val { Unsigned(usize), Signed(isize), Bool(bool) } +enum Val { + Unsigned(usize), + Signed(isize), + Bool(bool), +} decodes_to!(decode_int, "1", (usize,), vec![(1usize,)]); decodes_to!(decode_many_int, "1,2", (usize, i16), vec![(1usize, 2i16)]); -decodes_to!(decode_float, "1,1.0,1.5", - (f64, f64, f64), vec![(1f64, 1.0, 1.5)]); +decodes_to!( + decode_float, + "1,1.0,1.5", + (f64, f64, f64), + vec![(1f64, 1.0, 1.5)] +); decodes_to!(decode_char, "a", char, vec![('a')]); decodes_to!(decode_str, "abc", (String,), vec![("abc".into(),)]); @@ -172,13 +256,24 @@ decodes_to!(decode_opt_empty, "\"\"", (Option,), vec![(None,)]); // vec![(Val::Bool(false), Val::Signed(-5), Val::Unsigned(5))]); decodes_to!(decode_opt_val, "1.0", (Option,), vec![(None,)]); -decodes_to!(decode_tail, "abc,1,2,3,4", (String, Vec), - vec![("abc".into(), vec![1usize, 2, 3, 4])]); +decodes_to!( + decode_tail, + "abc,1,2,3,4", + (String, Vec), + vec![("abc".into(), vec![1usize, 2, 3, 4])] +); #[derive(Debug, RustcDecodable, RustcEncodable, PartialEq, Eq)] -enum MyEnum { Enum1, Enum2 } -decodes_to!(decode_myenum, "Enum1,Enum1,Enum2", (MyEnum, MyEnum, MyEnum), - vec![(MyEnum::Enum1, MyEnum::Enum1, MyEnum::Enum2)]); +enum MyEnum { + Enum1, + Enum2, +} +decodes_to!( + decode_myenum, + "Enum1,Enum1,Enum2", + (MyEnum, MyEnum, MyEnum), + vec![(MyEnum::Enum1, MyEnum::Enum1, MyEnum::Enum2)] +); #[test] fn no_headers_no_skip_one_record() { let mut d = Csv::from_string("a,b"); @@ -193,7 +288,10 @@ fn no_headers_first_record() { let r = d.headers(); assert_eq!(r, Vec::::new()); let r = d.next().unwrap().unwrap(); - assert_eq!(r.columns().unwrap().collect::>(), vec!("a".to_string(), "b".to_string())); + assert_eq!( + r.columns().unwrap().collect::>(), + vec!("a".to_string(), "b".to_string()) + ); } #[test] @@ -220,7 +318,6 @@ fn utf8_bom_quotes() { assert_eq!(c, vec![b"abc", b"xyz"]); } - #[test] fn byte_strings_invalid_utf8() { let mut d = Csv::from_reader(&b"a\xffbc,xyz"[..]); @@ -243,14 +340,18 @@ fn seeking() { { let d = Csv::from_reader(Read::by_ref(&mut buf)); - let vals = d.map(|r| r.unwrap().decode::<(usize, usize)>().unwrap()).collect::>(); + let vals = d + .map(|r| r.unwrap().decode::<(usize, usize)>().unwrap()) + .collect::>(); assert_eq!(vals, vec!((1, 2), (3, 4), (5, 6))); } buf.seek(io::SeekFrom::Start(0)).unwrap(); { let d = Csv::from_reader(Read::by_ref(&mut buf)); - let vals = d.map(|r| r.unwrap().decode::<(usize, usize)>().unwrap()).collect::>(); + let vals = d + .map(|r| r.unwrap().decode::<(usize, usize)>().unwrap()) + .collect::>(); assert_eq!(vals, vec!((1, 2), (3, 4), (5, 6))); } }