dctap/
tap_reader_builder.rs1use crate::reader_range::ReaderRange;
2use crate::{
3 TapConfig,
5 TapError,
6 TapReader,
7 TapReaderState,
8};
9use crate::{tap_error::Result, tap_headers::TapHeaders};
10use calamine::{Reader as XlsxReader, Xlsx, open_workbook};
11use csv::ReaderBuilder;
12use std::fs::File;
13use std::io::{
15 self,
16 BufReader,
17 };
19use std::path::Path;
20
21#[derive(Default)]
22pub struct TapReaderBuilder {
23 _reader_builder: ReaderBuilder,
24}
25
26impl TapReaderBuilder {
27 pub fn new() -> TapReaderBuilder {
28 TapReaderBuilder::default()
29 }
30 pub fn from_path<P: AsRef<Path>>(path: P, config: &TapConfig) -> Result<TapReader<File>> {
76 let mut reader = ReaderBuilder::new()
77 .delimiter(config.delimiter())
78 .quote(config.quote())
79 .flexible(config.flexible())
80 .from_path(path)?;
81 let rcd_headers = reader.headers()?;
82 let headers = TapHeaders::from_record(rcd_headers)?;
83 let state = TapReaderState::new().with_headers(headers);
84 Ok(TapReader::new_csv_reader(reader, state, config))
85 }
86
87 pub fn from_reader<R: io::Read>(rdr: R, config: &TapConfig) -> Result<TapReader<R>> {
88 let mut reader = ReaderBuilder::new()
89 .delimiter(config.delimiter())
90 .quote(config.quote())
91 .flexible(config.flexible())
92 .from_reader(rdr);
93 let rcd_headers = reader.headers()?;
94 let headers = TapHeaders::from_record(rcd_headers)?;
95 let state = TapReaderState::new().with_headers(headers);
96 Ok(TapReader::new_csv_reader(reader, state, config))
97 }
98
99 pub fn from_excel<R: io::Read, P: AsRef<Path>>(
100 path: P,
101 sheet_name: Option<&str>,
102 config: &TapConfig,
103 ) -> Result<TapReader<R>> {
104 let path_name = path.as_ref().to_string_lossy().to_string();
105 let mut excel: Xlsx<_> = match open_workbook(path) {
106 Ok(xls) => Ok::<calamine::Xlsx<BufReader<File>>, TapError>(xls),
107 Err(e) => Err(TapError::OpeningWorkbook {
108 path: path_name.clone(),
109 error: e,
110 }),
111 }?;
112 let range = match sheet_name {
113 None => match excel.worksheet_range_at(0) {
114 Some(range) => range.map_err(|e| TapError::Sheet0Error {
115 path: path_name.clone(),
116 error: e,
117 }),
118 None => Err(TapError::Sheet0NotFound {
119 path: path_name.clone(),
120 }),
121 },
122 Some(name) => excel
123 .worksheet_range(name)
124 .map_err(|e| TapError::SheetNameError {
125 path: path_name.clone(),
126 sheet_name: name.to_string(),
127 error: e,
128 }),
129 }?;
130 let mut reader_range: ReaderRange<R> = ReaderRange::new(range);
131 if let Some(rcd) = reader_range.next_record() {
132 let headers = TapHeaders::from_record(&rcd)?;
133 let state = TapReaderState::new().with_headers(headers);
134 Ok(TapReader::new_range_reader(reader_range, state, config))
135 } else {
136 Err(TapError::NoHeadersExcel {
137 path: path_name.clone(),
138 })
139 }
140 }
141}