calamine/
auto.rs

1//! A module to convert file extension to reader
2
3use crate::errors::Error;
4use crate::vba::VbaProject;
5use crate::{
6    open_workbook, open_workbook_from_rs, DataType, Metadata, Ods, Range, Reader, Xls, Xlsb, Xlsx,
7};
8use std::borrow::Cow;
9use std::fs::File;
10use std::io::BufReader;
11use std::path::Path;
12
13/// A wrapper over all sheets when the file type is not known at static time
14pub enum Sheets<RS> {
15    /// Xls reader
16    Xls(Xls<RS>),
17    /// Xlsx reader
18    Xlsx(Xlsx<RS>),
19    /// Xlsb reader
20    Xlsb(Xlsb<RS>),
21    /// Ods reader
22    Ods(Ods<RS>),
23}
24
25/// Opens a workbook and define the file type at runtime.
26///
27/// Whenever possible use the statically known `open_workbook` function instead
28pub fn open_workbook_auto<P>(path: P) -> Result<Sheets<BufReader<File>>, Error>
29where
30    P: AsRef<Path>,
31{
32    let path = path.as_ref();
33    Ok(match path.extension().and_then(|e| e.to_str()) {
34        Some("xls") | Some("xla") => Sheets::Xls(open_workbook(path).map_err(Error::Xls)?),
35        Some("xlsx") | Some("xlsm") | Some("xlam") => {
36            Sheets::Xlsx(open_workbook(path).map_err(Error::Xlsx)?)
37        }
38        Some("xlsb") => Sheets::Xlsb(open_workbook(path).map_err(Error::Xlsb)?),
39        Some("ods") => Sheets::Ods(open_workbook(path).map_err(Error::Ods)?),
40        _ => {
41            if let Ok(ret) = open_workbook::<Xls<_>, _>(path) {
42                return Ok(Sheets::Xls(ret));
43            } else if let Ok(ret) = open_workbook::<Xlsx<_>, _>(path) {
44                return Ok(Sheets::Xlsx(ret));
45            } else if let Ok(ret) = open_workbook::<Xlsb<_>, _>(path) {
46                return Ok(Sheets::Xlsb(ret));
47            } else if let Ok(ret) = open_workbook::<Ods<_>, _>(path) {
48                return Ok(Sheets::Ods(ret));
49            } else {
50                return Err(Error::Msg("Cannot detect file format"));
51            };
52        }
53    })
54}
55
56/// Opens a workbook from the given bytes.
57///
58/// Whenever possible use the statically known `open_workbook_from_rs` function instead
59pub fn open_workbook_auto_from_rs<RS>(data: RS) -> Result<Sheets<RS>, Error>
60where
61    RS: std::io::Read + std::io::Seek + Clone,
62{
63    if let Ok(ret) = open_workbook_from_rs::<Xls<RS>, RS>(data.clone()) {
64        return Ok(Sheets::Xls(ret));
65    } else if let Ok(ret) = open_workbook_from_rs::<Xlsx<RS>, RS>(data.clone()) {
66        return Ok(Sheets::Xlsx(ret));
67    } else if let Ok(ret) = open_workbook_from_rs::<Xlsb<RS>, RS>(data.clone()) {
68        return Ok(Sheets::Xlsb(ret));
69    } else if let Ok(ret) = open_workbook_from_rs::<Ods<RS>, RS>(data) {
70        return Ok(Sheets::Ods(ret));
71    } else {
72        return Err(Error::Msg("Cannot detect file format"));
73    };
74}
75
76impl<RS> Reader<RS> for Sheets<RS>
77where
78    RS: std::io::Read + std::io::Seek,
79{
80    type Error = Error;
81
82    /// Creates a new instance.
83    fn new(_reader: RS) -> Result<Self, Self::Error> {
84        Err(Error::Msg("Sheets must be created from a Path"))
85    }
86
87    /// Gets `VbaProject`
88    fn vba_project(&mut self) -> Option<Result<Cow<'_, VbaProject>, Self::Error>> {
89        match *self {
90            Sheets::Xls(ref mut e) => e.vba_project().map(|vba| vba.map_err(Error::Xls)),
91            Sheets::Xlsx(ref mut e) => e.vba_project().map(|vba| vba.map_err(Error::Xlsx)),
92            Sheets::Xlsb(ref mut e) => e.vba_project().map(|vba| vba.map_err(Error::Xlsb)),
93            Sheets::Ods(ref mut e) => e.vba_project().map(|vba| vba.map_err(Error::Ods)),
94        }
95    }
96
97    /// Initialize
98    fn metadata(&self) -> &Metadata {
99        match *self {
100            Sheets::Xls(ref e) => e.metadata(),
101            Sheets::Xlsx(ref e) => e.metadata(),
102            Sheets::Xlsb(ref e) => e.metadata(),
103            Sheets::Ods(ref e) => e.metadata(),
104        }
105    }
106
107    /// Read worksheet data in corresponding worksheet path
108    fn worksheet_range(&mut self, name: &str) -> Option<Result<Range<DataType>, Self::Error>> {
109        match *self {
110            Sheets::Xls(ref mut e) => e.worksheet_range(name).map(|r| r.map_err(Error::Xls)),
111            Sheets::Xlsx(ref mut e) => e.worksheet_range(name).map(|r| r.map_err(Error::Xlsx)),
112            Sheets::Xlsb(ref mut e) => e.worksheet_range(name).map(|r| r.map_err(Error::Xlsb)),
113            Sheets::Ods(ref mut e) => e.worksheet_range(name).map(|r| r.map_err(Error::Ods)),
114        }
115    }
116
117    /// Read worksheet formula in corresponding worksheet path
118    fn worksheet_formula(&mut self, name: &str) -> Option<Result<Range<String>, Self::Error>> {
119        match *self {
120            Sheets::Xls(ref mut e) => e.worksheet_formula(name).map(|r| r.map_err(Error::Xls)),
121            Sheets::Xlsx(ref mut e) => e.worksheet_formula(name).map(|r| r.map_err(Error::Xlsx)),
122            Sheets::Xlsb(ref mut e) => e.worksheet_formula(name).map(|r| r.map_err(Error::Xlsb)),
123            Sheets::Ods(ref mut e) => e.worksheet_formula(name).map(|r| r.map_err(Error::Ods)),
124        }
125    }
126
127    fn worksheets(&mut self) -> Vec<(String, Range<DataType>)> {
128        match *self {
129            Sheets::Xls(ref mut e) => e.worksheets(),
130            Sheets::Xlsx(ref mut e) => e.worksheets(),
131            Sheets::Xlsb(ref mut e) => e.worksheets(),
132            Sheets::Ods(ref mut e) => e.worksheets(),
133        }
134    }
135
136    #[cfg(feature = "picture")]
137    fn pictures(&self) -> Option<Vec<(String, Vec<u8>)>> {
138        match *self {
139            Sheets::Xls(ref e) => e.pictures(),
140            Sheets::Xlsx(ref e) => e.pictures(),
141            Sheets::Xlsb(ref e) => e.pictures(),
142            Sheets::Ods(ref e) => e.pictures(),
143        }
144    }
145}