mule 0.5.0

Strong-headed (yet flexible) parser of columnar datasets from CSV, TSV and other delimiter-separated datasets
Documentation
use derive_more::Display;
use mule::{Dataset, DatasetValue, Parsed, RawValue, ReadingOptions, Result, Typer};
use std::env;

#[tokio::main]
pub async fn main() -> Result<()> {
    let file_path = env::args()
        .skip(1)
        .next()
        .unwrap_or_else(|| "datasets/sales-10.tsv".to_string());
    let options = ReadingOptions::default();
    let typer = CustomTyper::default();
    let dataset = Dataset::read_file(file_path, options, &typer).await?;

    println!("Got dataset: {:#?}", dataset);

    Ok(())
}

#[derive(Clone, PartialEq, Debug)]
pub enum CustomValue {
    Maybe(YayNay),
    Int(i64),
    Float(f64),
    Text(String),
}

impl DatasetValue<CustomColumnType> for CustomValue {
    fn get_column_type(&self) -> CustomColumnType {
        match self {
            CustomValue::Maybe(_) => CustomColumnType::Maybe,
            CustomValue::Int(_) => CustomColumnType::Int,
            CustomValue::Float(_) => CustomColumnType::Float,
            CustomValue::Text(_) => CustomColumnType::Text,
        }
    }
}

#[derive(PartialEq, Debug, Eq, Hash, Clone, Copy)]
pub enum YayNay {
    Yay,
    Nay,
    Nah,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Display)]
pub enum CustomColumnType {
    Maybe,
    Int,
    Float,
    Text,
    Unknown,
}

impl Default for CustomColumnType {
    fn default() -> Self {
        CustomColumnType::Unknown
    }
}

#[derive(Default, Debug, Clone)]
pub struct CustomTyper;

impl CustomTyper {
    fn as_int(&self, value: &RawValue) -> Parsed<CustomValue> {
        value.parse_i64().map(CustomValue::Int)
    }

    fn as_float(&self, value: &RawValue) -> Parsed<CustomValue> {
        value.parse_f64().map(CustomValue::Float)
    }

    fn as_text(&self, value: &RawValue) -> CustomValue {
        CustomValue::Text(value.0.to_string())
    }

    fn as_maybe(&self, value: &RawValue) -> Parsed<CustomValue> {
        match value.0.to_lowercase().trim() {
            "" => Parsed::Missing,
            "yay" => Parsed::Some(CustomValue::Maybe(YayNay::Yay)),
            "nay" => Parsed::Some(CustomValue::Maybe(YayNay::Nay)),
            _ => Parsed::Invalid,
        }
    }
}

impl Typer for CustomTyper {
    type ColumnType = CustomColumnType;
    type DatasetValue = CustomValue;

    const COLUMN_TYPES: &'static [Self::ColumnType] = &[
        CustomColumnType::Maybe,
        CustomColumnType::Int,
        CustomColumnType::Float,
        CustomColumnType::Text,
    ];

    fn parse_as(&self, value: &RawValue, tag: Self::ColumnType) -> Parsed<Self::DatasetValue> {
        match tag {
            CustomColumnType::Maybe => self.as_maybe(value),
            CustomColumnType::Int => self.as_int(value),
            CustomColumnType::Float => self.as_float(value),
            CustomColumnType::Text => Parsed::Some(self.as_text(value)),
            CustomColumnType::Unknown => Parsed::Invalid,
        }
    }
}