easy_sqlite/impls/
table_manager.rs

1use rusqlite::Connection;
2use std::marker::PhantomData;
3
4use crate::entities::errors::{DbError, DbResult};
5use crate::entities::types::SStr;
6use crate::traits::repo::{IConnection, IDbRepo, IExecutor, INewDbRepo};
7use crate::traits::table::ITable;
8
9pub struct TableManager<Cnn, Tbl> {
10    pub connection: Cnn,
11    table: PhantomData<Tbl>,
12}
13
14impl<Cnn: Clone, Tbl> Clone for TableManager<Cnn, Tbl> {
15    fn clone(&self) -> Self {
16        Self {
17            connection: self.connection.clone(),
18            table: Default::default(),
19        }
20    }
21}
22
23impl<Cnn, Tbl: ITable> TableManager<Cnn, Tbl> {
24    fn create_query(&self) -> String {
25        let columns = Tbl::COLUMNS
26            .iter()
27            .map(|(c_name, c_type)| format!("`{}` {}", c_name, c_type));
28        let unique = Tbl::UNIQUE.iter().map(Self::wrap_unique);
29        let f_keys = Tbl::FOREIGN_KEYS.iter().map(|(column, table, ext_column)| {
30            format!(
31                "FOREIGN KEY (`{}`) REFERENCES `{}`(`{}`)",
32                column, table, ext_column,
33            )
34        });
35        let attrs = columns
36            .chain(unique)
37            .chain(f_keys)
38            .collect::<Vec<_>>()
39            .join(", ");
40        format!("CREATE TABLE IF NOT EXISTS `{}` ({})", Tbl::NAME, attrs,)
41    }
42
43    fn wrap_unique(fields: &SStr) -> String {
44        let fields = fields
45            .split(',')
46            .map(|field| format!("`{}`", field.trim()))
47            .collect::<Vec<_>>()
48            .join(",");
49        format!("UNIQUE({})", fields)
50    }
51
52    fn make_index_name(&self, field: &str) -> String {
53        format!("{}_{}_index", Tbl::NAME, field)
54    }
55}
56
57impl<Cnn, Tbl: ITable> INewDbRepo<Cnn> for TableManager<Cnn, Tbl> {
58    fn create(connection: Cnn) -> Self {
59        Self {
60            connection,
61            table: Default::default(),
62        }
63    }
64}
65
66impl<Cnn: IConnection + IExecutor, Tbl: ITable> IDbRepo for TableManager<Cnn, Tbl> {
67    fn init(&self) -> DbResult<()> {
68        match self.connection.execute(&self.create_query(), &[]) {
69            Err(DbError::Other(msg)) => Err(DbError::CanNotInitTable(Tbl::NAME, msg)),
70            other => other,
71        }
72    }
73
74    fn drop(&self) -> DbResult<()> {
75        let query = format!("DROP TABLE IF EXISTS `{}`", Tbl::NAME);
76        self.connection.execute(&query, &[])
77    }
78
79    fn set_indexes(&self) -> DbResult<()> {
80        let fun = |cnn: &Connection| -> DbResult<()> {
81            for column in Tbl::INDEXES.iter() {
82                let index = self.make_index_name(column);
83                let query = format!(
84                    "CREATE INDEX IF NOT EXISTS {} ON `{}` (`{}`)",
85                    index,
86                    Tbl::NAME,
87                    column,
88                );
89                cnn.execute(&query, ())?;
90            }
91            Ok(())
92        };
93        self.connection.with(fun)
94    }
95
96    fn drop_indexes(&self) -> DbResult<()> {
97        let fun = |cnn: &Connection| -> DbResult<()> {
98            for column in Tbl::INDEXES.iter() {
99                let index = self.make_index_name(column);
100                let query = format!("DROP INDEX IF EXISTS {}", index);
101                cnn.execute(&query, ())?;
102            }
103            Ok(())
104        };
105        self.connection.with(fun)
106    }
107
108    fn get_size(&self) -> DbResult<usize> {
109        let query = format!("SELECT COUNT(*) FROM `{}`", Tbl::NAME);
110        let fun = move |cnn: &Connection| -> DbResult<usize> {
111            let mut stm = cnn.prepare(&query)?;
112            let mut rows = stm.raw_query();
113            Ok(rows.next()?.unwrap().get(0)?)
114        };
115        self.connection.with(fun)
116    }
117}