rocket_auth2 0.6.2

A high level authentication management library for Rocket applications. It supports both SQLite and Postgres.
Documentation
mod sql;

use crate::prelude::{Result, *};
use crate::user::roles::Roles;
use rocket::async_trait;
use sql::*;
use std::borrow::Cow;
use tokio::sync::Mutex;

#[cfg(feature = "rusqlite")]
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput};
#[cfg(feature = "rusqlite")]
use rusqlite::Row;
#[cfg(feature = "rusqlite")]
use rusqlite::*;
#[cfg(feature = "rusqlite")]
use std::convert::{TryFrom, TryInto};
#[cfg(feature = "rusqlite")]
use tokio::task::block_in_place;

#[cfg(feature = "rusqlite")]
impl FromSql for Roles {
    fn column_result(value: rusqlite::types::ValueRef<'_>) -> FromSqlResult<Self> {
        match value {
            rusqlite::types::ValueRef::Blob(bytes) => {
                Ok(bson::from_slice(bytes).map_err(|e| FromSqlError::Other(Box::new(e)))?)
            }
            _ => Err(FromSqlError::InvalidType),
        }
    }
}

#[cfg(feature = "rusqlite")]
impl ToSql for Roles {
    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
        let bytes = bson::to_vec(self).map_err(|e| FromSqlError::Other(Box::new(e)))?;
        Ok(bytes.into())
    }
}

#[cfg(feature = "rusqlite")]
impl<'a> TryFrom<&rusqlite::Row<'a>> for crate::User {
    type Error = rusqlite::Error;
    fn try_from(row: &Row) -> Result<User, rusqlite::Error> {
        Ok(User {
            id: row.get(0)?,
            email: row.get(1)?,
            password: row.get(2)?,
            roles: row.get(3)?,
        })
    }
}

#[cfg(feature = "rusqlite")]
#[async_trait]
impl DBConnection for Mutex<rusqlite::Connection> {
    async fn init(&self) -> Result<()> {
        let conn = self.lock().await;
        block_in_place(|| conn.execute(CREATE_TABLE, []))?;
        Ok(())
    }

    async fn create_user(&self, email: &str, hash: &str, roles: &Roles) -> Result<()> {
        let conn = self.lock().await;
        block_in_place(|| conn.execute(INSERT_USER, params![email, hash, roles]))?;

        Ok(())
    }

    async fn update_user(&self, user: &User) -> Result<()> {
        let conn = self.lock().await;
        block_in_place(|| {
            conn.execute(
                UPDATE_USER,
                params![user.id, user.email, user.password, user.roles],
            )
        })?;
        Ok(())
    }

    async fn delete_user_by_id(&self, user_id: i32) -> Result<()> {
        let conn = self.lock().await;
        block_in_place(|| conn.execute(REMOVE_BY_ID, params![user_id]))?;
        Ok(())
    }

    async fn delete_user_by_email(&self, email: &str) -> Result<()> {
        let conn = self.lock().await;
        block_in_place(|| conn.execute(REMOVE_BY_EMAIL, params![email]))?;
        Ok(())
    }

    async fn get_user_by_id(&self, user_id: i32) -> Result<User> {
        let conn = self.lock().await;
        let user = block_in_place(|| {
            conn.query_row(
                SELECT_BY_ID, //
                params![user_id],
                |row| row.try_into(),
            )
        })?;
        Ok(user)
    }

    async fn get_user_by_email(&self, email: &str) -> Result<User> {
        let conn = self.lock().await;
        let user = block_in_place(|| {
            conn.query_row(
                SELECT_BY_EMAIL, //
                params![email],
                |row| row.try_into(),
            )
        })?;
        Ok(user)
    }
    async fn get_all_ids(&self) -> Result<Vec<i32>> {
        let conn = self.lock().await;
        let mut stmt = conn.prepare(GET_ALL)?;
        let ids = block_in_place(|| -> Result<Vec<i32>> {
            Ok(stmt
                .query_map([], |row| row.get::<usize, i32>(0))?
                .flatten()
                .collect())
        })?;
        Ok(ids)
    }
}

#[cfg(feature = "sqlx-sqlite")]
use sqlx::encode::IsNull;
#[cfg(feature = "sqlx-sqlite")]
use sqlx::error::BoxDynError;
use sqlx::sqlite::SqliteArgumentValue;
#[cfg(feature = "sqlx-sqlite")]
use sqlx::{sqlite::SqliteConnection, *};

#[cfg(feature = "sqlx-sqlite")]
impl Type<Sqlite> for Roles {
    fn type_info() -> <Sqlite as Database>::TypeInfo {
        <[u8] as Type<Sqlite>>::type_info()
    }
}

#[cfg(feature = "sqlx-sqlite")]
impl<'q> Encode<'q, Sqlite> for Roles {
    fn encode_by_ref(
        &self,
        buf: &mut <Sqlite as Database>::ArgumentBuffer<'q>,
    ) -> std::result::Result<IsNull, BoxDynError> {
        let bytes = bson::to_vec(self)?;
        buf.push(SqliteArgumentValue::Blob(Cow::Owned(bytes)));
        Ok(IsNull::No)
    }
}

#[cfg(feature = "sqlx-sqlite")]
impl<'q> Decode<'q, Sqlite> for Roles {
    fn decode(value: <Sqlite as Database>::ValueRef<'q>) -> std::result::Result<Self, BoxDynError> {
        let bytes = <&[u8] as Decode<Sqlite>>::decode(value)?;
        Ok(bson::from_slice(bytes)?)
    }
}

#[cfg(feature = "sqlx-sqlite")]
#[async_trait]
impl DBConnection for Mutex<SqliteConnection> {
    async fn init(&self) -> Result<()> {
        let mut db = self.lock().await;
        query(CREATE_TABLE).execute(&mut *db).await?;
        println!("table created");
        Ok(())
    }
    async fn create_user(&self, email: &str, hash: &str, roles: &Roles) -> Result<()> {
        let mut db = self.lock().await;
        query(INSERT_USER)
            .bind(email)
            .bind(hash)
            .bind(roles)
            .execute(&mut *db)
            .await?;
        Ok(())
    }
    async fn update_user(&self, user: &User) -> Result<()> {
        let mut db = self.lock().await;
        query(UPDATE_USER)
            .bind(user.id)
            .bind(&user.email)
            .bind(&user.password)
            .bind(&user.roles)
            .execute(&mut *db)
            .await?;
        Ok(())
    }
    async fn delete_user_by_id(&self, user_id: i32) -> Result<()> {
        query(REMOVE_BY_ID)
            .bind(user_id)
            .execute(&mut *self.lock().await)
            .await?;
        Ok(())
    }
    async fn delete_user_by_email(&self, email: &str) -> Result<()> {
        query(REMOVE_BY_EMAIL)
            .bind(email)
            .execute(&mut *self.lock().await)
            .await?;
        Ok(())
    }
    async fn get_user_by_id(&self, user_id: i32) -> Result<User> {
        let mut db = self.lock().await;

        let user = query_as(SELECT_BY_ID)
            .bind(user_id)
            .fetch_one(&mut *db)
            .await?;

        Ok(user)
    }
    async fn get_user_by_email(&self, email: &str) -> Result<User> {
        let mut db = self.lock().await;
        let user = query_as(SELECT_BY_EMAIL)
            .bind(email)
            .fetch_one(&mut *db)
            .await?;
        Ok(user)
    }
    async fn get_all_ids(&self) -> Result<Vec<i32>> {
        let mut db = self.lock().await;
        let ids = query_scalar(GET_ALL).fetch_all(&mut *db).await?;
        Ok(ids)
    }
}
#[cfg(feature = "sqlx-sqlite")]
#[rocket::async_trait]
impl DBConnection for SqlitePool {
    async fn init(&self) -> Result<()> {
        query(CREATE_TABLE) //
            .execute(self)
            .await?;
        Ok(())
    }
    async fn create_user(&self, email: &str, hash: &str, roles: &Roles) -> Result<()> {
        query(INSERT_USER)
            .bind(email)
            .bind(hash)
            .bind(roles)
            .execute(self)
            .await?;
        Ok(())
    }
    async fn update_user(&self, user: &User) -> Result<()> {
        query(UPDATE_USER)
            .bind(user.id)
            .bind(&user.email)
            .bind(&user.password)
            .bind(&user.roles)
            .execute(self)
            .await?;
        Ok(())
    }
    async fn delete_user_by_id(&self, user_id: i32) -> Result<()> {
        query(REMOVE_BY_ID) //
            .bind(user_id)
            .execute(self)
            .await?;
        Ok(())
    }
    async fn delete_user_by_email(&self, email: &str) -> Result<()> {
        query(REMOVE_BY_EMAIL) //
            .bind(email)
            .execute(self)
            .await?;
        Ok(())
    }
    async fn get_user_by_id(&self, user_id: i32) -> Result<User> {
        let user = query_as(SELECT_BY_ID) //
            .bind(user_id)
            .fetch_one(self)
            .await?;
        Ok(user)
    }
    async fn get_user_by_email(&self, email: &str) -> Result<User> {
        let user = query_as(SELECT_BY_EMAIL).bind(email).fetch_one(self).await;
        println!("user: {:?}", user);
        Ok(user?)
    }
    async fn get_all_ids(&self) -> Result<Vec<i32>> {
        let ids = query_scalar(GET_ALL).fetch_all(self).await?;
        Ok(ids)
    }
}