use std::fmt;
use std::fs::File;
use std::io::{Read, Seek};
use std::path::Path;
use csv::{Reader, ReaderBuilder};
use error::*;
use field_type::Type;
use snip::snip_preamble;
#[derive(Debug, Clone, PartialEq)]
pub struct Metadata {
pub dialect: Dialect,
pub num_fields: usize,
pub types: Vec<Type>,
}
impl fmt::Display for Metadata {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Metadata")?;
writeln!(f, "========")?;
writeln!(f, "{}", self.dialect)?;
writeln!(f, "Number of fields: {}", self.num_fields)?;
writeln!(f, "Types:")?;
for (i, ty) in self.types.iter().enumerate() {
writeln!(f, "\t{}: {}", i, ty)?;
}
Ok(())
}
}
#[derive(Clone)]
pub struct Dialect {
pub delimiter: u8,
pub header: Header,
pub quote: Quote,
pub flexible: bool,
}
impl PartialEq for Dialect {
fn eq(&self, other: &Dialect) -> bool {
self.delimiter == other.delimiter
&& self.header == other.header
&& self.quote == other.quote
&& self.flexible == other.flexible
}
}
impl fmt::Debug for Dialect {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Dialect")
.field("delimiter", &char::from(self.delimiter))
.field("header", &self.header)
.field("quote", &self.quote)
.field("flexible", &self.flexible)
.finish()
}
}
impl fmt::Display for Dialect {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Dialect:")?;
writeln!(f, "\tDelimiter: {}", char::from(self.delimiter))?;
writeln!(f, "\tHas header row?: {}", self.header.has_header_row)?;
writeln!(
f,
"\tNumber of preamble rows: {}",
self.header.num_preamble_rows
)?;
writeln!(
f,
"\tQuote character: {}",
match self.quote {
Quote::Some(chr) => format!("{}", char::from(chr)),
Quote::None => "none".into(),
}
)?;
writeln!(f, "\tFlexible: {}", self.flexible)
}
}
impl Dialect {
pub fn open_path<P: AsRef<Path>>(&self, path: P) -> Result<Reader<File>> {
self.open_reader(File::open(path)?)
}
pub fn open_reader<R: Read + Seek>(&self, mut rdr: R) -> Result<Reader<R>> {
snip_preamble(&mut rdr, self.header.num_preamble_rows)?;
let bldr: ReaderBuilder = self.clone().into();
Ok(bldr.from_reader(rdr))
}
}
impl From<Dialect> for ReaderBuilder {
fn from(dialect: Dialect) -> ReaderBuilder {
let mut bldr = ReaderBuilder::new();
bldr.delimiter(dialect.delimiter)
.has_headers(dialect.header.has_header_row)
.flexible(dialect.flexible);
match dialect.quote {
Quote::Some(character) => {
bldr.quoting(true);
bldr.quote(character);
}
Quote::None => {
bldr.quoting(false);
}
}
bldr
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Header {
pub has_header_row: bool,
pub num_preamble_rows: usize,
}
#[derive(Clone, PartialEq)]
pub enum Quote {
None,
Some(u8),
}
impl fmt::Debug for Quote {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Quote::Some(ref character) => f
.debug_struct("Some")
.field("character", &char::from(*character))
.finish(),
Quote::None => write!(f, "None"),
}
}
}
#[derive(Clone, PartialEq)]
pub enum Escape {
Enabled(u8),
Disabled,
}
impl From<Escape> for Option<u8> {
fn from(escape: Escape) -> Option<u8> {
match escape {
Escape::Enabled(chr) => Some(chr),
Escape::Disabled => None,
}
}
}
impl fmt::Debug for Escape {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Escape::Enabled(chr) => write!(f, "Enabled({})", char::from(chr)),
Escape::Disabled => write!(f, "Disabled"),
}
}
}
#[derive(Clone, PartialEq)]
pub enum Comment {
Enabled(u8),
Disabled,
}
impl From<Comment> for Option<u8> {
fn from(comment: Comment) -> Option<u8> {
match comment {
Comment::Enabled(chr) => Some(chr),
Comment::Disabled => None,
}
}
}
impl fmt::Debug for Comment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Comment::Enabled(chr) => write!(f, "Enabled({})", char::from(chr)),
Comment::Disabled => write!(f, "Disabled"),
}
}
}