use rusqlite::fallible_iterator::FallibleIterator; use rusqlite::{Connection, Error, Row}; use std::sync::{Arc, Mutex, MutexGuard}; use log::warn; use uuid::Uuid; use lumiere_domain::user::model::User; #[derive(Clone)] pub struct UserSqliteRepository { pub database_connection: Arc>, } impl UserSqliteRepository { pub fn new(database_connection: Arc>) -> Self { Self { database_connection, } } fn get_connection(&self) -> MutexGuard<'_, Connection> { self.database_connection.lock().unwrap() } pub fn create_table(&self) -> Result<(), Error> { let connection = self.get_connection(); connection.execute( "CREATE TABLE IF NOT EXISTS \"user\" ( id TEXT PRIMARY KEY, username VARCHAR NOT NULL, encrypted_password VARCHAR NOT NULL );", [] )?; Ok(()) } pub fn insert(&self, user: &User) -> rusqlite::Result<()> { let database_connection = self.get_connection(); let mut prepared_statement = database_connection .prepare( "INSERT INTO \"user\" ( id, username, encrypted_password ) VALUES (?1, ?2, ?3);", ) .expect("Unable to prepare statement for user saving…"); prepared_statement.execute(rusqlite::params![ user.id.to_string(), user.username, user.encrypted_password ]).expect("Unable to insert user."); Ok(()) } pub fn update(&self, user: &User) -> rusqlite::Result<()> { let database_connection = self.get_connection(); let mut prepared_statement = database_connection .prepare( "UPDATE \"user\" SET username = ?1, encrypted_password = ?2 WHERE id = ?3;", ) .expect("Unable to prepare statement for user saving…"); prepared_statement.execute(rusqlite::params![ user.username, user.encrypted_password, user.id.to_string() ]).expect("Unable to update user."); Ok(()) } pub fn delete_by_id(&self, id: &Uuid) -> rusqlite::Result<()> { let database_connection = self.get_connection(); let mut prepared_statement = database_connection .prepare("DELETE FROM \"user\" WHERE id = ?1;") .expect("Unable to prepare statement for user deletion…"); prepared_statement.execute(rusqlite::params![id.to_string()]) .expect("Unable to delete the user…"); Ok(()) } pub fn count(&self) -> rusqlite::Result { let database_connection = self.get_connection(); let mut prepared_statement = database_connection .prepare("SELECT COUNT(1) FROM \"user\";") .expect("Unable to prepare statement for user counting…"); let mut rows = prepared_statement.query([])?; if let Some(row) = rows.next()? { Ok(row.get(0)?) } else { Ok(0) } } pub fn get_by_id(&self, id: &Uuid) -> rusqlite::Result> { let database_connection = self.get_connection(); let mut prepared_statement = database_connection.prepare( "SELECT id, username, encrypted_password FROM \"user\"\ WHERE id = ?1;" )?; let mut rows = prepared_statement.query([id.to_string()])?; if let Some(row) = rows.next()? { Ok(Some(Self::map_to_user(&row)?)) } else { Ok(None) } } pub fn exists_by_name(&self, username: &String) -> rusqlite::Result { let database_connection = self.get_connection(); let mut prepared_statement = database_connection.prepare( "SELECT EXISTS( SELECT 1 FROM \"user\" WHERE username = ?1\ );" )?; let mut rows = prepared_statement.query([username])?; if let Some(row) = rows.next()? { Ok(row.get(0)?) } else { warn!("No result for exists_by_name repository function."); Ok(false) } } pub fn get_by_username(&self, username: &String) -> rusqlite::Result> { let database_connection = self.get_connection(); let mut prepared_statement = database_connection.prepare( "SELECT id, username, encrypted_password FROM \"user\"\ WHERE username = ?1;" )?; let mut rows = prepared_statement.query([username])?; if let Some(row) = rows.next()? { Ok(Some(Self::map_to_user(&row)?)) } else { Ok(None) } } fn map_to_user(row: &Row) -> Result { let id_text: String = row.get(0)?; Ok(User { id: Uuid::parse_str(&id_text).unwrap(), username: row.get(1)?, encrypted_password: row.get(2)?, }) } pub fn get_all(&self) -> Result, Error> { let database_connection = self.get_connection(); let mut prepared_statement = database_connection.prepare( "SELECT id, username, encrypted_password FROM \"user\"", )?; let rows = prepared_statement.query([])?; rows.map(UserSqliteRepository::map_to_user).collect::>() } }