use std::fs::File;
use std::io::{BufReader, Read, Seek};
use std::path::Path;
use crate::sheet::{Workbook, Worksheet, WorksheetIterator, CellValue, Result as SheetResult};
use super::iterators::TextWorksheetIterator;
#[derive(Debug, Clone)]
pub struct TextConfig {
pub delimiter: u8,
pub quote: u8,
pub comment: Option<u8>,
pub trim_whitespace: bool,
pub has_headers: bool,
pub max_line_length: usize,
pub buffer_size: usize,
}
impl Default for TextConfig {
fn default() -> Self {
Self {
delimiter: b',', quote: b'"', comment: Some(b'#'), trim_whitespace: false, has_headers: true, max_line_length: 1024 * 1024, buffer_size: 8192, }
}
}
impl TextConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_delimiter(mut self, delimiter: u8) -> Self {
self.delimiter = delimiter;
self
}
pub fn with_quote(mut self, quote: u8) -> Self {
self.quote = quote;
self
}
pub fn with_comment(mut self, comment: Option<u8>) -> Self {
self.comment = comment;
self
}
pub fn with_trim_whitespace(mut self, trim: bool) -> Self {
self.trim_whitespace = trim;
self
}
pub fn with_headers(mut self, has_headers: bool) -> Self {
self.has_headers = has_headers;
self
}
pub fn with_max_line_length(mut self, max_len: usize) -> Self {
self.max_line_length = max_len;
self
}
pub fn with_buffer_size(mut self, size: usize) -> Self {
self.buffer_size = size;
self
}
pub fn tsv() -> Self {
Self::new().with_delimiter(b'\t')
}
pub fn prn() -> Self {
Self::new().with_delimiter(b';')
}
pub fn pipe() -> Self {
Self::new().with_delimiter(b'|')
}
}
pub struct TextWorkbook {
data: Vec<Vec<CellValue>>,
config: TextConfig,
worksheet_name: String,
}
impl TextWorkbook {
pub fn open<P: AsRef<Path>>(path: P) -> SheetResult<Self> {
Self::from_path_with_config(path, TextConfig::default())
}
pub fn from_path_with_config<P: AsRef<Path>>(path: P, config: TextConfig) -> SheetResult<Self> {
let file = File::open(path)?;
let mut reader = BufReader::with_capacity(config.buffer_size, file);
Self::from_reader(&mut reader, config)
}
pub fn from_reader<R: Read + Seek>(reader: &mut R, config: TextConfig) -> SheetResult<Self> {
let mut parser = super::parser::TextParser::new(reader, config.clone());
let mut data = Vec::new();
while let Some(row_result) = parser.parse_row()? {
data.push(row_result?);
}
let worksheet_name = "Sheet1".to_string();
Ok(TextWorkbook {
data,
config,
worksheet_name,
})
}
pub fn from_bytes(bytes: &[u8], config: TextConfig) -> SheetResult<Self> {
let mut cursor = std::io::Cursor::new(bytes);
Self::from_reader(&mut cursor, config)
}
pub fn config(&self) -> &TextConfig {
&self.config
}
pub fn worksheet_name(&self) -> &str {
&self.worksheet_name
}
pub fn set_worksheet_name(&mut self, name: String) {
self.worksheet_name = name;
}
}
impl Workbook for TextWorkbook {
fn active_worksheet(&self) -> SheetResult<Box<dyn Worksheet + '_>> {
Ok(Box::new(super::worksheet::TextWorksheet::from_data(
&self.data,
self.worksheet_name.clone(),
)))
}
fn worksheet_names(&self) -> Vec<String> {
vec![self.worksheet_name.clone()]
}
fn worksheet_by_name(&self, name: &str) -> SheetResult<Box<dyn Worksheet + '_>> {
if name == self.worksheet_name {
self.active_worksheet()
} else {
Err(format!("Worksheet '{}' not found", name).into())
}
}
fn worksheet_by_index(&self, index: usize) -> SheetResult<Box<dyn Worksheet + '_>> {
match index {
0 => self.active_worksheet(),
_ => Err(format!("Worksheet index {} out of range", index).into()),
}
}
fn worksheets(&self) -> Box<dyn WorksheetIterator<'_> + '_> {
Box::new(TextWorksheetIterator::new(self))
}
fn worksheet_count(&self) -> usize {
1
}
fn active_sheet_index(&self) -> usize {
0
}
}