rec23-rs 0.2.82

A library for REC23 CRM.
Documentation
use std::error::Error;
use bb8_postgres::bb8::Pool;
use bb8_postgres::PostgresConnectionManager;
use bb8_postgres::tokio_postgres::{GenericClient, NoTls, Row};
use paperclip::actix::Apiv2Schema;
use serde::{Deserialize, Serialize};

const GET_USER_BY_TELEGRAM_ID: &str = "SELECT employee.id, employee.name, employee.dept_id, employee.post_id, employee.telegram_name, employee.telegram_id FROM rec23.employee employee WHERE employee.telegram_id = $1";
const GET_USER_BY_TELEGRAM_NAME: &str = "SELECT employee.id, employee.name, employee.dept_id, employee.post_id, employee.telegram_name, employee.telegram_id FROM rec23.employee employee WHERE employee.telegram_name = $1";
const GET_USER_BY_ID: &str = "SELECT employee.id, employee.name, employee.dept_id, employee.post_id, employee.telegram_name, employee.telegram_id FROM rec23.employee employee WHERE employee.id = $1";
const GET_DEFAULT_ASSIGNEE_EMP_ID: &str = "SELECT assignee_emp.default_emp_id FROM rec23.assignee_emp assignee_emp WHERE assignee_emp.assignee_id = $1";
const WRITE_TO_DATABASE: &str = "UPDATE rec23.employee SET telegram_id = $2 WHERE id = $1";
const GET: &str = "SELECT id, name, dept_id, post_id, telegram_name, telegram_id FROM rec23.employee";
const DELETE: &str = "DELETE FROM rec23.employee WHERE id = $1";
const UPDATE_TELEGRAM_NAME: &str = "UPDATE rec23.employee SET telegram_id = NULL, telegram_name = $2 WHERE id = $1";
const UPDATE_DEPT: &str = "UPDATE rec23.employee SET dept_id = $2, post_id = $3 WHERE id = $1";

#[derive(Serialize, Deserialize, Apiv2Schema)]
pub struct User {
    pub id: i32,
    pub name: String,
    pub dept_id: Option<i32>,
    pub post_id: Option<i32>,
    pub telegram_name: Option<String>,
    pub telegram_id: Option<i32>
}

impl User {
    pub async fn get(pool: &Pool<PostgresConnectionManager<NoTls>>) -> Result<Vec<Self>, Box<dyn Error + Sync + Send>> {
        let connection = pool.get().await?;
        let rows = connection.query(GET, &[]).await?;
        Ok(rows.iter().map(|row|Self::convert_from_row(row)).collect())
    }
    /// This method is responsible for retrieving a user's record from the database based on their Telegram id.
    ///
    /// # Arguments
    ///
    /// * `telegram_id` - A 32-bit integer that represents the desired user's Telegram id.
    /// * `pool` - A shared connection pool manager for making requests to the PostgreSQL database.
    ///
    /// # Returns
    ///
    /// This function returns a Result object that contains Option<Rec23User> and Box<dyn Error>.
    /// If succeeded, it will return a Some<Rec23User> value indicating that a record was found and successfully converted to a Rec23User object.
    /// If no record is found, it will return None indicating that no records were found in the database for the supplied Telegram id.
    /// In case of an error, it will return an Err variant enclosing a dynamic error.
    ///
    pub async fn get_user_by_telegram_id(telegram_id: i32, pool: &Pool<PostgresConnectionManager<NoTls>>) -> Result<Option<Self>, Box<dyn Error + Sync + Send>> {
        let connection = pool.get().await?;
        let client = connection.client();
        let row = client.query_opt(GET_USER_BY_TELEGRAM_ID, &[&telegram_id]).await?;
        let user = match row {
            Some(row) => Some(Self::convert_from_row(&row)),
            None => None,
        };
        Ok(user)
    }

    pub async fn get_user_by_telegram_name(telegram_name: String, pool: &Pool<PostgresConnectionManager<NoTls>>) -> Result<Option<Self>, Box<dyn Error + Sync + Send>> {
        let connection = pool.get().await?;
        let client = connection.client();
        let row = client.query_opt(GET_USER_BY_TELEGRAM_NAME, &[&telegram_name]).await?;
        let user = match row {
            Some(row) => Some(Self::convert_from_row(&row)),
            None => None,
        };
        Ok(user)
    }

    /// This method retrieves a user's record from the database based on their ID.
    ///
    /// # Arguments
    ///
    /// * `id` - A 32-bit integer that represents the desired user's ID.
    /// * `pool` - A shared connection pool manager for making requests to the PostgreSQL database.
    ///
    /// # Returns
    ///
    /// This function returns a Result object that contains `Self` (Rec23User instance) and Box<dyn Error>.
    /// If succeeded, it will return a `Rec23User` value indicating that a record was found and successfully converted to a `Rec23User` object.
    /// If the method encounters an error during the execution, it will return an Err variant enclosing a dynamic error.
    ///
    /// The function leverages an asynchronous programming model, as denoted by the `async` keyword, allowing for non-blocking operations.
    /// It uses the `tokio` library to perform asynchronous operations.
    ///
    /// Note: This method assumes that there will always be a record in the database for the supplied ID. If no record was found, it would return an error.
    pub async fn get_user_by_id(id: i32, pool: &Pool<PostgresConnectionManager<NoTls>>) -> Result<Option<Self>, Box<dyn Error + Sync + Send>> {
        let connection = pool.get().await?;
        let client = connection.client();
        return match client.query_opt(GET_USER_BY_ID, &[&id]).await? {
            Some(value) => Ok(Some(Self::convert_from_row(&value))),
            None => Ok(None)
        };
    }

    // pub async fn get_user_by_id_opt(id: Option<i32>, pool: &Pool<PostgresConnectionManager<NoTls>>) -> Result<Option<Self>, Box<dyn Error>> {
    //     match id {
    //         Some(id_val) => {
    //             let connection = pool.get().await?;
    //             let client = connection.client();
    //             let row = client.query_opt(GET_USER_BY_ID, &[&id_val]).await?;
    //             match row {
    //                 Some(row_val) => Ok(Some(Self::convert_from_row(&row_val))),
    //                 None => Ok(None),
    //             }
    //         }
    //         None => Ok(None),
    //     }
    // }

    /// This method is responsible for converting a Row object from a PostgreSQL query into a Rec23User object.
    ///
    /// # Arguments
    ///
    /// * `row` - A reference to the Row object retrieved from the PostgreSQL query. It contains the fields of the matching database record.
    ///
    /// # Returns
    ///
    /// This method has no side effects and returns a Rec23User object constructed from the given Row object. Each field of the Rec23User struct
    /// corresponds to a Column in the Row object. The method uses the `get` function provided by the Row object to map the columns to the struct fields.
    ///
    /// If the Row object does not contain a column that corresponds to any of the Rec23User's fields or the value cannot be converted to the desired type,
    /// the `get` function will panic at runtime.
    pub fn convert_from_row(row: &Row) -> Self {
        Self {
            id: row.get("id"),
            name: row.get("name"),
            dept_id: row.get("dept_id"),
            post_id: row.get("post_id"),
            telegram_name: row.get("telegram_name"),
            telegram_id: row.get("telegram_id"),
        }
    }

    /// This method is responsible for retrieving the default assignee employee id from the database based on the user id.
    ///
    /// # Arguments
    ///
    /// * `self` - Ownership of the current instance of the `Rec23User` struct.
    /// * `pool` - A shared connection pool manager for making requests to the PostgreSQL database.
    ///
    /// # Returns
    ///
    /// This function returns a Result object that contains an Option<i32> value and Box<dyn Error>.
    /// The Option<i32> represents the default assignee employee id. If no default assignee employee id is found in the database for the supplied user id, it returns None.
    /// In case of an error during the database operations, the function returns an Err variant enclosing a dynamic error.
    ///
    /// This method fetches a connection from the pool using the `get` method. It then creates a `GenericClient`, which is used to execute SQL commands in the database.
    /// This method executes a query that fetches the default assignee employee id using the user id and the 'GET_DEFAULT_ASSIGNEE_EMP_ID' constant SQL command.
    /// The `query_one` method is used to fetch single row of data, and it returns a Row object, which represents a single row of a result set of a query.
    /// It assumes that query returns only one row and in case of multiple rows or no rows found, it will return an error.
    /// The function then maps the returned row to `i32` type data using the `get` method on the returned row instance with **0** as the argument.
    ///
    /// Note: The method consumes the Rec23User instance because `self` is taken by value. If it is necessary to keep the original instance,
    /// consider refactoring the method to take `&self` or `&mut self` as an argument.
    pub async fn get_default_assignee_emp_id(self, pool: &Pool<PostgresConnectionManager<NoTls>>) -> Result<Option<i32>, Box<dyn Error + Sync + Send>> {
        let connection = pool.get().await?;
        let client = connection.client();
        let row = match client.query_opt(GET_DEFAULT_ASSIGNEE_EMP_ID, &[&self.id]).await? {
            Some(row) => row,
            None => return Ok(None)
        };
        Ok(Some(row.get(0)))
    }

    pub async fn write_to_database(&self, pool: &Pool<PostgresConnectionManager<NoTls>>)->Result<(), Box<dyn Error+Send+Sync>>{
        let connection = pool.get().await?;
        connection.execute(WRITE_TO_DATABASE, &[&self.id, &self.telegram_id]).await?;
        Ok(())
    }

    pub async fn delete(id: i32, pool: &Pool<PostgresConnectionManager<NoTls>>)->Result<(), Box<dyn Error+Send+Sync>>{
        let connection = pool.get().await?;
        connection.execute(DELETE, &[&id]).await?;
        Ok(())
    }

    pub async fn update_telegram_tag(&self, pool: &Pool<PostgresConnectionManager<NoTls>>)->Result<(), Box<dyn Error+Send+Sync>>{
        let connection = pool.get().await?;
        connection.execute(UPDATE_TELEGRAM_NAME, &[&self.id, &self.telegram_name]).await?;
        Ok(())
    }

    pub async fn update_dept(&self, pool: &Pool<PostgresConnectionManager<NoTls>>)->Result<(), Box<dyn Error+Send+Sync>>{
        let connection = pool.get().await?;
        connection.execute(UPDATE_DEPT, &[&self.id, &self.dept_id, &self.post_id]).await?;
        Ok(())
    }
}