dctap/tap_reader_builder.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
use crate::{tap_error::Result, tap_headers::TapHeaders};
use crate::{ReaderRange, TapConfig, TapError, TapReader, TapReaderState};
use calamine::{open_workbook, Reader as XlsxReader, Xlsx};
use csv::ReaderBuilder;
use std::fs::File;
// use indexmap::IndexSet;
use std::io::{self, BufReader};
use std::path::Path;
#[derive(Default)]
pub struct TapReaderBuilder {
_reader_builder: ReaderBuilder,
}
impl TapReaderBuilder {
pub fn new() -> TapReaderBuilder {
TapReaderBuilder::default()
}
/*
// Most of these options are copied from CSV Rust
pub fn _flexible(mut self, yes: bool) -> Self {
self.reader_builder.flexible(yes);
self
}
pub fn _trim(&mut self, trim: Trim) -> &mut TapReaderBuilder {
self.reader_builder.trim(trim);
self
}
pub fn _terminator(&mut self, term: Terminator) -> &mut TapReaderBuilder {
self.reader_builder.terminator(term);
self
}
pub fn _quote(&mut self, quote: u8) -> &mut TapReaderBuilder {
self.reader_builder.quote(quote);
self
}
pub fn _delimiter(&mut self, delimiter: u8) -> &mut TapReaderBuilder {
self.reader_builder.delimiter(delimiter);
self
}
*/
/// Build a TapReader from a path and a `TapConfig`
///
/// # Example
/// ```no_run
/// use dctap::TapReaderBuilder;
/// use dctap::TapConfig;
/// use std::error::Error;
///
/// # fn main() { example().unwrap(); }
/// fn example() -> Result<(), Box<dyn Error>> {
/// let mut tap = TapReaderBuilder::from_path("foo.csv", &TapConfig::default())?;
/// for result in tap.shapes() {
/// let shape = result?;
/// println!("{:?}", shape);
/// }
/// Ok(())
/// }
/// ```
pub fn from_path<P: AsRef<Path>>(path: P, config: &TapConfig) -> Result<TapReader<File>> {
let mut reader = ReaderBuilder::new()
.delimiter(config.delimiter())
.quote(config.quote())
.flexible(config.flexible())
.from_path(path)?;
let rcd_headers = reader.headers()?;
let headers = TapHeaders::from_record(rcd_headers)?;
let state = TapReaderState::new().with_headers(headers);
Ok(TapReader::new_csv_reader(reader, state, config))
}
pub fn from_reader<R: io::Read>(rdr: R, config: &TapConfig) -> Result<TapReader<R>> {
let mut reader = ReaderBuilder::new()
.delimiter(config.delimiter())
.quote(config.quote())
.flexible(config.flexible())
.from_reader(rdr);
let rcd_headers = reader.headers()?;
let headers = TapHeaders::from_record(rcd_headers)?;
let state = TapReaderState::new().with_headers(headers);
Ok(TapReader::new_csv_reader(reader, state, config))
}
pub fn from_excel<R: io::Read, P: AsRef<Path>>(
path: P,
sheet_name: Option<&str>,
config: &TapConfig,
) -> Result<TapReader<R>> {
let path_name = path.as_ref().to_string_lossy().to_string();
let mut excel: Xlsx<_> = match open_workbook(path) {
Ok(xls) => Ok::<calamine::Xlsx<BufReader<File>>, TapError>(xls),
Err(e) => Err(TapError::OpeningWorkbook {
path: path_name.clone(),
error: e,
}),
}?;
let range = match sheet_name {
None => match excel.worksheet_range_at(0) {
Some(range) => range.map_err(|e| TapError::Sheet0Error {
path: path_name.clone(),
error: e,
}),
None => Err(TapError::Sheet0NotFound {
path: path_name.clone(),
}),
},
Some(name) => excel
.worksheet_range(name)
.map_err(|e| TapError::SheetNameError {
path: path_name.clone(),
sheet_name: name.to_string(),
error: e,
}),
}?;
let mut reader_range: ReaderRange<R> = ReaderRange::new(range);
if let Some(rcd) = reader_range.next_record() {
let headers = TapHeaders::from_record(&rcd)?;
let state = TapReaderState::new().with_headers(headers);
Ok(TapReader::new_range_reader(reader_range, state, config))
} else {
Err(TapError::NoHeadersExcel {
path: path_name.clone(),
})
}
}
}