dctap/
tap_reader_builder.rs

1use crate::reader_range::ReaderRange;
2use crate::{
3    // ReaderRange,
4    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;
13// use indexmap::IndexSet;
14use std::io::{
15    self,
16    BufReader,
17    // BufReader
18};
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    /*
31        // Most of these options are copied from CSV Rust
32        pub fn _flexible(mut self, yes: bool) -> Self {
33            self.reader_builder.flexible(yes);
34            self
35        }
36
37        pub fn _trim(&mut self, trim: Trim) -> &mut TapReaderBuilder {
38            self.reader_builder.trim(trim);
39            self
40        }
41
42        pub fn _terminator(&mut self, term: Terminator) -> &mut TapReaderBuilder {
43            self.reader_builder.terminator(term);
44            self
45        }
46
47        pub fn _quote(&mut self, quote: u8) -> &mut TapReaderBuilder {
48            self.reader_builder.quote(quote);
49            self
50        }
51
52        pub fn _delimiter(&mut self, delimiter: u8) -> &mut TapReaderBuilder {
53            self.reader_builder.delimiter(delimiter);
54            self
55        }
56    */
57    /// Build a TapReader from a path and a `TapConfig`
58    ///
59    /// # Example
60    /// ```no_run
61    /// use dctap::TapReaderBuilder;
62    /// use dctap::TapConfig;
63    /// use std::error::Error;
64    ///
65    /// # fn main() { example().unwrap(); }
66    /// fn example() -> Result<(), Box<dyn Error>> {
67    ///     let mut tap = TapReaderBuilder::from_path("foo.csv", &TapConfig::default())?;
68    ///     for result in tap.shapes() {
69    ///         let shape = result?;
70    ///         println!("{:?}", shape);
71    ///     }
72    ///     Ok(())
73    /// }
74    /// ```
75    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}