csvsql 0.1.4

SQL like engine that works on CSV file.
Documentation
use std::{
    collections::{HashMap, hash_map::Entry},
    rc::Rc,
};

use crate::{
    results::{Column, ColumnIndexError, Name},
    util::SmartReference,
};

pub enum Metadata {
    Simple(SimpleResultSetMetadata),
    Product(ProductResultSetMetadata),
    Grouped {
        parent: Rc<Metadata>,
        this: Box<Metadata>,
    },
}
impl Metadata {
    pub fn column_index(
        &self,
        name: &Name,
    ) -> Result<SmartReference<'_, Column>, ColumnIndexError> {
        match self {
            Metadata::Simple(data) => data.column_index(name),
            Metadata::Product(data) => data.column_index(name),
            Metadata::Grouped { parent: _, this } => this.column_index(name),
        }
    }
    pub fn column_name(&self, column: &Column) -> Option<&Name> {
        match self {
            Metadata::Simple(data) => data.column_name(column),
            Metadata::Product(data) => data.column_name(column),
            Metadata::Grouped { parent: _, this } => this.column_name(column),
        }
    }
    pub fn column_title(&self, column: &Column) -> &str {
        self.column_name(column)
            .map(|n| n.short_name())
            .unwrap_or_default()
    }
    pub fn number_of_columns(&self) -> usize {
        match self {
            Metadata::Simple(data) => data.columns.len(),
            Metadata::Product(data) => {
                data.left.number_of_columns() + data.right.number_of_columns()
            }
            Metadata::Grouped { parent: _, this } => this.number_of_columns(),
        }
    }
    pub fn result_name(&self) -> Option<&Name> {
        match self {
            Metadata::Simple(data) => data.name.as_ref(),
            Metadata::Product(_) => None,
            Metadata::Grouped { parent: _, this } => this.result_name(),
        }
    }
    pub(crate) fn product(left: &Rc<Self>, right: &Rc<Self>) -> Self {
        let left = left.clone();
        let right = right.clone();
        Metadata::Product(ProductResultSetMetadata { left, right })
    }

    pub fn columns(&self) -> Box<dyn Iterator<Item = Column>> {
        Box::new((0..self.number_of_columns()).map(Column::from_index))
    }
}
pub struct ProductResultSetMetadata {
    left: Rc<Metadata>,
    right: Rc<Metadata>,
}
impl ProductResultSetMetadata {
    fn column_index(
        &self,
        name: &crate::results::Name,
    ) -> Result<SmartReference<'_, Column>, ColumnIndexError> {
        let left_result = self.left.column_index(name);
        let right_result = self.right.column_index(name);
        match (&left_result, &right_result) {
            (Err(ColumnIndexError::AmbiguousColumnName(_)), _) => left_result,
            (_, Err(ColumnIndexError::AmbiguousColumnName(_))) => right_result,
            (Ok(_), Ok(_)) => Err(ColumnIndexError::AmbiguousColumnName(name.full_name())),
            (Ok(_), Err(ColumnIndexError::NoSuchColumn(_))) => left_result,
            (Err(ColumnIndexError::NoSuchColumn(_)), Ok(right_result)) => {
                let col =
                    Column::from_index(right_result.get_index() + self.left.number_of_columns());
                Ok(SmartReference::Owned(col))
            }
            (Err(ColumnIndexError::NoSuchColumn(_)), Err(ColumnIndexError::NoSuchColumn(_))) => {
                right_result
            }
        }
    }
    fn column_name(&self, column: &Column) -> Option<&Name> {
        self.left.column_name(column).or_else(|| {
            self.right.column_name(&Column::from_index(
                column.get_index() - self.left.number_of_columns(),
            ))
        })
    }
}

enum ColumnInResult {
    Column(Column),
    Ambiguous,
}

pub struct SimpleResultSetMetadata {
    columns: Vec<Name>,
    name: Option<Name>,
    column_names: HashMap<Name, ColumnInResult>,
}
impl SimpleResultSetMetadata {
    fn column_name(&self, column: &Column) -> Option<&Name> {
        self.columns.get(column.get_index())
    }
    fn column_index(&self, name: &Name) -> Result<SmartReference<'_, Column>, ColumnIndexError> {
        match self.column_names.get(name) {
            None => Err(ColumnIndexError::NoSuchColumn(name.full_name())),
            Some(ColumnInResult::Ambiguous) => {
                Err(ColumnIndexError::AmbiguousColumnName(name.full_name()))
            }
            Some(ColumnInResult::Column(c)) => Ok(SmartReference::Borrowed(c)),
        }
    }

    pub(crate) fn new(name: Option<Name>) -> Self {
        Self {
            columns: vec![],
            name,
            column_names: HashMap::new(),
        }
    }
    pub(crate) fn add_column(&mut self, name: &str) {
        let index = Column::from_index(self.columns.len());
        let full_name = match &self.name {
            Some(parent_name) => parent_name.append(name),
            None => name.into(),
        };
        self.set_name_to_index(&index, &full_name);
        self.columns.push(full_name);
    }
    pub(crate) fn build(self) -> Metadata {
        Metadata::Simple(self)
    }
    pub(crate) fn len(&self) -> usize {
        self.columns.len()
    }
    fn set_name_to_index(&mut self, index: &Column, name: &Name) {
        for name in name.available_names() {
            match self.column_names.entry(name) {
                Entry::Vacant(entry) => {
                    entry.insert(ColumnInResult::Column(index.clone()));
                }
                Entry::Occupied(mut entry) => {
                    entry.insert(ColumnInResult::Ambiguous);
                }
            };
        }
    }
}