easy-sqlite 0.4.1

Little sqlite framework
Documentation
use rusqlite::Connection;
use std::marker::PhantomData;

use crate::entities::errors::{DbError, DbResult};
use crate::entities::types::SStr;
use crate::traits::repo::{IConnection, IDbRepo, IExecutor, INewDbRepo};
use crate::traits::table::ITable;

pub struct TableManager<Cnn, Tbl> {
    pub connection: Cnn,
    table: PhantomData<Tbl>,
}

impl<Cnn: Clone, Tbl> Clone for TableManager<Cnn, Tbl> {
    fn clone(&self) -> Self {
        Self {
            connection: self.connection.clone(),
            table: Default::default(),
        }
    }
}

impl<Cnn, Tbl: ITable> TableManager<Cnn, Tbl> {
    fn create_query(&self) -> String {
        let columns = Tbl::COLUMNS
            .iter()
            .map(|(c_name, c_type)| format!("`{}` {}", c_name, c_type));
        let unique = Tbl::UNIQUE.iter().map(Self::wrap_unique);
        let f_keys = Tbl::FOREIGN_KEYS.iter().map(|(column, table, ext_column)| {
            format!(
                "FOREIGN KEY (`{}`) REFERENCES `{}`(`{}`)",
                column, table, ext_column,
            )
        });
        let attrs = columns
            .chain(unique)
            .chain(f_keys)
            .collect::<Vec<_>>()
            .join(", ");
        format!("CREATE TABLE IF NOT EXISTS `{}` ({})", Tbl::NAME, attrs,)
    }

    fn wrap_unique(fields: &SStr) -> String {
        let fields = fields
            .split(',')
            .map(|field| format!("`{}`", field.trim()))
            .collect::<Vec<_>>()
            .join(",");
        format!("UNIQUE({})", fields)
    }

    fn make_index_name(&self, field: &str) -> String {
        format!("{}_{}_index", Tbl::NAME, field)
    }
}

impl<Cnn, Tbl: ITable> INewDbRepo<Cnn> for TableManager<Cnn, Tbl> {
    fn create(connection: Cnn) -> Self {
        Self {
            connection,
            table: Default::default(),
        }
    }
}

impl<Cnn: IConnection + IExecutor, Tbl: ITable> IDbRepo for TableManager<Cnn, Tbl> {
    fn init(&self) -> DbResult<()> {
        match self.connection.execute(&self.create_query(), &[]) {
            Err(DbError::Other(msg)) => Err(DbError::CanNotInitTable(Tbl::NAME, msg)),
            other => other,
        }
    }

    fn drop(&self) -> DbResult<()> {
        let query = format!("DROP TABLE IF EXISTS `{}`", Tbl::NAME);
        self.connection.execute(&query, &[])
    }

    fn set_indexes(&self) -> DbResult<()> {
        let fun = |cnn: &Connection| -> DbResult<()> {
            for column in Tbl::INDEXES.iter() {
                let index = self.make_index_name(column);
                let query = format!(
                    "CREATE INDEX IF NOT EXISTS {} ON `{}` (`{}`)",
                    index,
                    Tbl::NAME,
                    column,
                );
                cnn.execute(&query, ())?;
            }
            Ok(())
        };
        self.connection.with(fun)
    }

    fn drop_indexes(&self) -> DbResult<()> {
        let fun = |cnn: &Connection| -> DbResult<()> {
            for column in Tbl::INDEXES.iter() {
                let index = self.make_index_name(column);
                let query = format!("DROP INDEX IF EXISTS {}", index);
                cnn.execute(&query, ())?;
            }
            Ok(())
        };
        self.connection.with(fun)
    }

    fn get_size(&self) -> DbResult<usize> {
        let query = format!("SELECT COUNT(*) FROM `{}`", Tbl::NAME);
        let fun = move |cnn: &Connection| -> DbResult<usize> {
            let mut stm = cnn.prepare(&query)?;
            let mut rows = stm.raw_query();
            Ok(rows.next()?.unwrap().get(0)?)
        };
        self.connection.with(fun)
    }
}