csvsql 0.1.4

SQL like engine that works on CSV file.
Documentation
use std::{collections::HashMap, path::PathBuf};

use tempfile::NamedTempFile;

use crate::{error::CvsSqlError, results::Name, transaction::Transaction};

#[derive(Default)]
pub(crate) struct Session {
    temporary_tables: TemporaryFiles,
    pub(crate) transaction: Option<Transaction>,
}

#[derive(Default)]
pub(crate) struct TemporaryFiles {
    temporary_tables: HashMap<String, NamedTempFile>,
}

impl TemporaryFiles {
    pub(crate) fn create_temporary_table(&mut self, name: &Name) -> Result<PathBuf, CvsSqlError> {
        let name = name.full_name();
        if self.temporary_tables.contains_key(&name) {
            return Err(CvsSqlError::TableAlreadyExists(name));
        }
        let temporary_file = NamedTempFile::with_suffix(".csv")?;
        let path = temporary_file.path().to_path_buf();

        self.temporary_tables.insert(name, temporary_file);

        Ok(path)
    }

    pub(crate) fn drop_temporary_table(&mut self, name: &Name) -> Result<(), CvsSqlError> {
        let name = name.full_name();
        if self.temporary_tables.remove(&name).is_none() {
            Err(CvsSqlError::TableNotExists(name))
        } else {
            Ok(())
        }
    }

    pub(crate) fn get_temporary_table(&self, name: &Name) -> Option<PathBuf> {
        let name = name.full_name();
        let file = self.temporary_tables.get(&name)?;
        Some(file.path().to_path_buf())
    }

    pub(crate) fn commit(&mut self, session: &mut TemporaryFiles) {
        for (name, file) in self.temporary_tables.drain() {
            session.temporary_tables.insert(name, file);
        }
    }
}

impl Session {
    pub(crate) fn create_temporary_table(&mut self, name: &Name) -> Result<PathBuf, CvsSqlError> {
        if let Some(ref mut transaction) = self.transaction {
            transaction.temporary_tables.create_temporary_table(name)
        } else {
            self.temporary_tables.create_temporary_table(name)
        }
    }

    pub(crate) fn drop_temporary_table(&mut self, name: &Name) -> Result<(), CvsSqlError> {
        if let Some(ref mut transaction) = self.transaction {
            transaction.temporary_tables.drop_temporary_table(name)
        } else {
            self.temporary_tables.drop_temporary_table(name)
        }
    }

    pub(crate) fn get_temporary_table(&self, name: &Name) -> Option<PathBuf> {
        if let Some(transaction) = &self.transaction
            && let Some(path) = transaction.temporary_tables.get_temporary_table(name)
        {
            return Some(path);
        }
        self.temporary_tables.get_temporary_table(name)
    }

    pub(crate) fn start_transaction(&mut self) -> Result<(), CvsSqlError> {
        if self.transaction.is_some() {
            return Err(CvsSqlError::TransactionInProgress);
        }
        self.transaction = Some(Transaction::default());
        Ok(())
    }

    pub(crate) fn commit_transaction(&mut self) -> Result<(), CvsSqlError> {
        let Some(ref mut transaction) = self.transaction.take() else {
            return Err(CvsSqlError::NoTransactionInProgress);
        };
        transaction
            .temporary_tables
            .commit(&mut self.temporary_tables);

        transaction.commit()?;

        Ok(())
    }

    pub(crate) fn rollback_transaction(&mut self) -> Result<(), CvsSqlError> {
        let Some(ref mut transaction) = self.transaction.take() else {
            return Err(CvsSqlError::NoTransactionInProgress);
        };
        transaction.rollback()?;

        Ok(())
    }
}