1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
#![doc = include_str!("../README.md")]
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
use serde::{Serialize, Deserialize};
use csv::{ByteRecord, ReaderBuilder};
use open::{assign_bytes, csv_read};
use errors::{CSVPerusalError, CellError};
use core::fmt;
pub mod errors;
pub mod utils;
pub mod open;
pub(crate) mod dates;
pub(crate) mod regex;
pub(crate) mod numbers;
pub(crate) mod types;
/// `CSVType` is the organized the data from [`csv::ByteRecord`].
/// It's meant to make the generic output data from the [`csv`] package easier to work with by giving each cell a specific data type.
/// Date, Time, and DateTime use [`chrono`]'s [`chrono::NaiveDate`], [`chrono::NaiveTime`], and [`chrono::NaiveDateTime`] respectively.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
pub enum CSVType {
Int(i64),
Float(f64),
String(String),
Date(NaiveDate),
Time(NaiveTime),
DateTime(NaiveDateTime),
Error(CellError),
Empty,
}
impl fmt::Display for CSVType {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, fmt)
}
}
/// Input the file path to a CSV file you want parsed and it will return a two-dimensional vector of [`CSVType`].
///
/// # Example
///
/// ```rust
/// use csv_perusal::{open_csv, CSVType};
///
/// fn main() {
/// let path = "test_data/MOCK_DATA.csv";
/// let data = open_csv(path).unwrap();
///
/// data.iter().for_each(|row| {
/// row.iter().for_each(|cell| {
/// match cell {
/// CSVType::Int(val) => print!("INT:{:?},", val),
/// CSVType::Float(val) => print!("FLOAT:{:?},", val),
/// CSVType::String(val) => print!("STRING:{:?},", val),
/// CSVType::Date(val) => print!("DATE:{:?},", val),
/// CSVType::Time(val) => print!("TIME:{:?},", val),
/// CSVType::DateTime(val) => print!("DATETIME:{:?},", val),
/// CSVType::Error(err) => print!("ERROR:{:?},", err),
/// CSVType::Empty => print!("NONE,"),
/// }
/// });
/// print!("\n");
/// );
/// }
/// ```
pub fn open_csv(path: &str) -> Result<Vec<Vec<CSVType>>, CSVPerusalError> {
let byte_records = csv_read(path)?;
byte_records.get_csv_types()
}
/// Contains the method for converting grid data to [`CSVType`].
pub trait ParseSeparatedValues {
/// A required method that converts grid data into a two-dimensional vector of [`CSVType`].
///
/// # How to Implement
///
/// ```rust
/// impl ParseSeparatedValues for Vec<Vec<MyCustomEnum>> {
/// fn get_csv_types(self) -> Result<Vec<Vec<CSVType>>, CSVPerusalError> {
/// // Make sure your input datatype can convert to a string.
/// // [`utils::grid_to_byterecord!`] will convert your grid data into Vec<[`csv::ByteRecord`]>.
/// let byte_records = grid_to_byterecord!(self);
/// // Converts Vec<[`csv::ByteRecord`]> to Vec<Vec<[`CSVType`]>>
/// assign_bytes(byte_records)
/// }
/// }
/// ```
fn get_csv_types(self) -> Result<Vec<Vec<CSVType>>, CSVPerusalError>;
}
impl ParseSeparatedValues for Vec<ByteRecord> {
/// Organizes [`csv::ByteRecord`] into specific data types via the [`CSVType`] struct.
fn get_csv_types(self) -> Result<Vec<Vec<CSVType>>, CSVPerusalError> {
assign_bytes(self)
}
}
impl ParseSeparatedValues for Vec<Vec<String>> {
/// Organizes two-dimensional [`String`] vector into specific data types via the [`CSVType`] struct.
fn get_csv_types(self) -> Result<Vec<Vec<CSVType>>, CSVPerusalError> {
let byte_records = grid_to_byterecord!(self);
assign_bytes(byte_records)
}
}
impl ParseSeparatedValues for Vec<Vec<&str>> {
/// Organizes two-dimensional [`str`] vector into specific data types via the [`CSVType`] struct.
fn get_csv_types(self) -> Result<Vec<Vec<CSVType>>, CSVPerusalError> {
let byte_records = grid_to_byterecord!(self);
assign_bytes(byte_records)
}
}