Skip to content

Commit

Permalink
Port libs to latest tabled
Browse files Browse the repository at this point in the history
  • Loading branch information
zhiburt committed Dec 12, 2023
1 parent 42ba493 commit bfb88f4
Show file tree
Hide file tree
Showing 17 changed files with 263 additions and 152 deletions.
2 changes: 1 addition & 1 deletion csv_to_table/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ color = ["tabled/color"]

[dependencies]
csv = "1"
tabled = { version = "0.12", features = ["std"], default-features = false }
tabled = { path = "../tabled", features = ["std"], default-features = false }

[dev-dependencies]
testing_table = { path = "../testing_table", features = ["color"] }
130 changes: 111 additions & 19 deletions csv_to_table/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,23 +68,57 @@ pub mod iter {
use super::*;
use std::fs::File;

pub use super::records::CsvRecords;

pub mod records {
//! A module which contains [`CsvRecords`].
pub use crate::records::*;
}
pub use super::records::*;

/// Creates [`IterTable`] from a csv [`Read`]er.
pub fn from_reader<R: Read>(reader: R) -> IterTable<CsvRecords<R>> {
///
/// # Example
///
/// ```
/// use csv_to_table::iter::from_reader;
///
/// let csv = r#"Name,Job Tittle,Number
/// Maxim,Plummer,12345
/// Alex,Sowftware Developer,45678"#;
///
/// let table = from_reader(csv.as_bytes());
///
/// let table = table.to_string();
///
/// assert_eq!(
/// table,
/// "+-------+---------------------+--------+\n\
/// | Name | Job Tittle | Number |\n\
/// +-------+---------------------+--------+\n\
/// | Maxim | Plummer | 12345 |\n\
/// +-------+---------------------+--------+\n\
/// | Alex | Sowftware Developer | 45678 |\n\
/// +-------+---------------------+--------+",
/// );
/// ```
pub fn from_reader<R>(reader: R) -> IterTable<CsvRecords<R>>
where
R: Read,
{
let rdr = ReaderBuilder::new().has_headers(false).from_reader(reader);

IterTable::new(CsvRecords::new(rdr))
}

/// Creates [`IterTable`] from a [`File`] which suppose to have a csv.
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<IterTable<CsvRecords<File>>, csv::Error> {
///
/// # Example
///
/// ```rust,no_run
/// use csv_to_table::iter::from_path;
///
/// let table = from_path("path/to/a/file").expect("success read");
/// let table = table.to_string();
/// ```
pub fn from_path<P>(path: P) -> Result<IterTable<CsvRecords<File>>, csv::Error>
where
P: AsRef<Path>,
{
let rdr = ReaderBuilder::new().has_headers(false).from_path(path)?;

let table = IterTable::new(CsvRecords::new(rdr));
Expand All @@ -93,9 +127,22 @@ pub mod iter {
}

/// Creates [`IterTable`] from a [`csv::Reader`].
pub fn from_csv_reader<R: Read>(
reader: Reader<R>,
) -> Result<IterTable<CsvRecords<R>>, csv::Error> {
///
/// # Example
///
/// ```rust,no_run
/// use csv_to_table::iter::from_csv_reader;
/// use csv::Reader;
///
/// let reader = Reader::from_path("path/to/a/file").expect("failed to find a file");
///
/// let table = from_csv_reader(reader).expect("failed to read from a reader");
/// let table = table.to_string();
/// ```
pub fn from_csv_reader<R>(reader: Reader<R>) -> Result<IterTable<CsvRecords<R>>, csv::Error>
where
R: Read,
{
let table = IterTable::new(CsvRecords::new(reader));

Ok(table)
Expand All @@ -104,32 +151,77 @@ pub mod iter {

/// Creates [`Table`] from [`Read`]er.
///
/// Notice that in case of big files you might better you [`iter::CsvRecords`].
pub fn from_reader<R: Read>(reader: R) -> Result<Table, csv::Error> {
/// Notice that in case of big files you might better use [`iter::CsvRecords`].
///
/// # Example
///
/// ```rust,no_run
/// use std::fs::File;
/// use csv_to_table::from_reader;
///
/// let file = File::open("/path/to/a/file").expect("failed to open a file");
///
/// let table = from_reader(file).expect("failed to read from a file");
/// let table = table.to_string();
/// ```
pub fn from_reader<R>(reader: R) -> Result<Table, csv::Error>
where
R: Read,
{
let rdr = ReaderBuilder::new().has_headers(false).from_reader(reader);

read_into_table(rdr)
}

/// Creates [`Table`] from a csv [`File`].
///
/// Notice that in case of big files you might better you [`iter::CsvRecords`].
/// Notice that in case of big files you might better use [`iter::CsvRecords`].
///
/// # Example
///
/// ```rust,no_run
/// use csv_to_table::from_path;
///
/// let table = from_path("path/to/a/file").expect("success read");
/// let table = table.to_string();
/// ```
///
/// [`File`]: std::fs::File
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Table, csv::Error> {
pub fn from_path<P>(path: P) -> Result<Table, csv::Error>
where
P: AsRef<Path>,
{
let rdr = ReaderBuilder::new().has_headers(false).from_path(path)?;

read_into_table(rdr)
}

/// Creates [`Table`] from a [`csv::Reader`].
///
/// Notice that in case of big files you might better you [`iter::CsvRecords`].
pub fn from_csv_reader<R: Read>(reader: Reader<R>) -> Result<Table, csv::Error> {
/// Notice that in case of big files you might better use [`iter::CsvRecords`].
///
/// # Example
///
/// ```rust,no_run
/// use csv_to_table::from_csv_reader;
/// use csv::Reader;
///
/// let reader = Reader::from_path("path/to/a/file").expect("failed to find a file");
///
/// let table = from_csv_reader(reader).expect("failed to read from a reader");
/// let table = table.to_string();
/// ```
pub fn from_csv_reader<R>(reader: Reader<R>) -> Result<Table, csv::Error>
where
R: Read,
{
read_into_table(reader)
}

fn read_into_table<R: Read>(reader: Reader<R>) -> Result<Table, csv::Error> {
fn read_into_table<R>(reader: Reader<R>) -> Result<Table, csv::Error>
where
R: Read,
{
let mut builder = Builder::default();

for record in reader.into_records() {
Expand Down
116 changes: 55 additions & 61 deletions csv_to_table/src/records.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
use std::{fmt::Debug, io::Read, mem::transmute};
use std::{fmt::Debug, io::Read};

use csv::{Reader, StringRecord, StringRecordsIntoIter};
use tabled::grid::records::IntoRecords;

/// A [`IntoRecords`] implementation for a [`csv::Reader`].
///
/// By default all errors are ignored, but you can print them using [`CsvRecords::print_errors`].
/// By default all errors are ignored,
/// but you can return them using [`CsvRecordsIter::set_catch`].
///
/// [`CsvRecords::print_errors`]: CsvRecords.print_errors
/// [`CsvRecordsIter::set_catch`]: CsvRecordsIter.set_catch
pub struct CsvRecords<R> {
rows: StringRecordsIntoIter<R>,
err_logic: ErrorLogic,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
enum ErrorLogic {
Ignore,
Print,
}

impl<R> CsvRecords<R> {
Expand All @@ -27,24 +21,61 @@ impl<R> CsvRecords<R> {
{
Self {
rows: reader.into_records(),
err_logic: ErrorLogic::Ignore,
}
}
}

/// Show underlying [Read] errors inside a table.
pub fn print_errors(mut self) -> Self {
self.err_logic = ErrorLogic::Print;
self
impl<R> IntoRecords for CsvRecords<R>
where
R: Read,
{
type Cell = String;
type IterColumns = CsvStringRecord;
type IterRows = CsvRecordsIter<R>;

fn iter_rows(self) -> Self::IterRows {
CsvRecordsIter {
iter: self.rows,
err_logic: ErrorLogic::Ignore,
err: None,
}
}
}

/// A row iterator.
pub struct CsvStringRecordsRows<R> {
pub struct CsvRecordsIter<R> {
iter: StringRecordsIntoIter<R>,
err_logic: ErrorLogic,
err: Option<std::io::Error>,
}

impl<R> Iterator for CsvStringRecordsRows<R>
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
enum ErrorLogic {
Ignore,
Catch,
}

impl<R> CsvRecordsIter<R> {
/// Return a status
///
/// It's a cought by a catcher you can set by [`CsvRecordsIter::set_catch`].
pub fn status(&self) -> Option<&std::io::Error> {
self.err.as_ref()
}

/// Show underlying [Read] errors inside a table.
pub fn set_catch(mut self, catch: bool) -> Self {
self.err_logic = if catch {
ErrorLogic::Catch
} else {
ErrorLogic::Ignore
};

self
}
}

impl<R> Iterator for CsvRecordsIter<R>
where
R: Read,
{
Expand All @@ -58,12 +89,9 @@ where
Ok(record) => return Some(CsvStringRecord::new(record)),
Err(err) => match self.err_logic {
ErrorLogic::Ignore => continue,
ErrorLogic::Print => {
let err = err.to_string();
let mut record = StringRecord::with_capacity(err.len(), 1);
record.push_field(&err);

return Some(CsvStringRecord::new(record));
ErrorLogic::Catch => {
self.err = Some(std::io::Error::from(err));
return None;
}
},
}
Expand All @@ -72,7 +100,7 @@ where
}

/// A column iterator.
#[derive(Debug)]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct CsvStringRecord {
record: StringRecord,
i: usize,
Expand All @@ -85,48 +113,14 @@ impl CsvStringRecord {
}

impl Iterator for CsvStringRecord {
type Item = CsvStrField;
type Item = String;

fn next(&mut self) -> Option<Self::Item> {
let text = self.record.get(self.i)?;
let text = unsafe { transmute::<&str, &'static str>(text) };
let text = String::from(text);

self.i += 1;
Some(CsvStrField(text))
}
}

/// A cell struct.
///
/// # SAFETY
///
/// NOTICE that it has a &'static lifetime which is not true.
/// It's made so only cause of trait limitations.
///
/// It's unsafe to keep the reference around.
///
/// It's OK for [`CsvRecords`] cause we do not keep it internally.
#[derive(Debug)]
pub struct CsvStrField(&'static str);

impl AsRef<str> for CsvStrField {
fn as_ref(&self) -> &str {
self.0
}
}

impl<R> IntoRecords for CsvRecords<R>
where
R: Read,
{
type Cell = CsvStrField;
type IterColumns = CsvStringRecord;
type IterRows = CsvStringRecordsRows<R>;

fn iter_rows(self) -> Self::IterRows {
CsvStringRecordsRows {
iter: self.rows,
err_logic: self.err_logic,
}
Some(text)
}
}
2 changes: 1 addition & 1 deletion json_to_table/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ color = ["tabled/color"]

[dependencies]
serde_json = "1"
tabled = { version = "0.12", features = ["macros"], default-features = false }
tabled = { path = "../tabled", features = ["macros"], default-features = false }

[dev-dependencies]
testing_table = { path = "../testing_table", features = ["color"] }
Loading

0 comments on commit bfb88f4

Please sign in to comment.