zero4rs 2.0.0

zero4rs is a powerful, pragmatic, and extremely fast web framework for Rust
Documentation
use lazy_static::lazy_static;

use anyhow::Context as _;
use serde::{Deserialize, Serialize};
use sqlx::mysql::MySqlRow;
use sqlx::{MySql, Pool, QueryBuilder, Row};

lazy_static! {
    #[rustfmt::skip]
    pub static ref SELECT_STATMENT: &'static str = "select f_id, f_user_id, f_app, f_name, f_digits, f_issuer, f_period, f_expired_at, f_key, f_code, f_created_at, f_updated_at from t_otp_code";
}

// use crate::core::error2::Error;
use crate::core::error2::Result;

#[rustfmt::skip]
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct OtpCodeRecord {
    pub id: u64,
    #[serde(skip_serializing_if = "String::is_empty")]
    pub user_id: String,
    #[serde(skip_serializing_if = "String::is_empty")]
    pub app: String,
    #[serde(skip_serializing_if = "String::is_empty")]
    pub name: String,
    pub digits: u32,
    #[serde(skip_serializing_if = "String::is_empty")]
    pub issuer: String,
    pub period: u32,
    pub expired_at: u64,
    #[serde(skip_serializing_if = "String::is_empty")]
    pub key: String,
    pub code: u32,
    pub created_at: u64,
    pub updated_at: u64,
}

#[rustfmt::skip]
#[allow(dead_code)]
impl OtpCodeRecord {
    pub fn new() -> Self {
        Self {
            ..Default::default()
        }
    }

    pub async fn select_by_primary_key(pool: &Pool<MySql>, id: &u64) -> Result<Option<MySqlRow>> {
        #[rustfmt::skip]
        let statment: &str = *SELECT_STATMENT;

        let mut query_builder: sqlx::QueryBuilder<sqlx::MySql> = sqlx::QueryBuilder::new(statment);

        query_builder.push(" where f_id = ");
        query_builder.push_bind(id);

        let row = query_builder
                .build()
                .fetch_optional(pool)
                .await
                .context("select_by_primary_key: Failed to perform a query to retrieve a otpcode.")?;

        Ok(row)
    }

    pub async fn insert(pool: &Pool<MySql>, record: &OtpCodeRecord) -> Result<u64> {
        let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new(
            "INSERT INTO t_otp_code (f_user_id, f_app, f_name, f_digits, f_issuer, f_period, f_expired_at, f_key, f_code, f_created_at, f_updated_at) ",
        );

        query_builder.push_values([record].iter(), |mut b, record| {
            b.push_bind(record.user_id.to_owned());
            b.push_bind(record.app.to_owned());
            b.push_bind(record.name.to_owned());
            b.push_bind(record.digits.to_owned());
            b.push_bind(record.issuer.to_owned());
            b.push_bind(record.period.to_owned());
            b.push_bind(record.expired_at.to_owned());
            b.push_bind(record.key.to_owned());
            b.push_bind(record.code.to_owned());
            b.push_bind(record.created_at.to_owned());
            b.push_bind(record.updated_at.to_owned());
        });

        let mut transaction = pool.begin().await.map_err(|e| {
            log::error!("insert-failed: error={:?}", e);
            anyhow::anyhow!(e)
        })?;

        let last_insert_id = query_builder
            .build()
            // .execute(pool)
            .execute(&mut *transaction)
            .await
            .map_err(|e| anyhow::anyhow!(e))?
            .last_insert_id();

        // if commit or rollback have not been called before the Transaction object goes out of scope (i.e. Drop is invoked),
        // a rollback command is queued to be executed as soon as an opportunity arises.
        transaction.commit().await.map_err(|e| {
            log::error!("insert-failed: error={:?}", e);
            anyhow::anyhow!(e)
        })?;

        Ok(last_insert_id)
    }

    pub async fn insert_bulk(pool: &Pool<MySql>, data: Vec<OtpCodeRecord>) -> Result<u64> {
        let mut insert_result_int = 0;

        for chunk in data.chunks(5000) {
            let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new(
                "INSERT INTO t_otp_code (f_user_id, f_app, f_name, f_digits, f_issuer, f_period, f_expired_at, f_key, f_code, f_created_at, f_updated_at) ",
            );

            query_builder.push_values(chunk.iter().take(5000 / 12), |mut b, record| {
                b.push_bind(record.user_id.to_owned());
                b.push_bind(record.app.to_owned());
                b.push_bind(record.name.to_owned());
                b.push_bind(record.digits.to_owned());
                b.push_bind(record.issuer.to_owned());
                b.push_bind(record.period.to_owned());
                b.push_bind(record.expired_at.to_owned());
                b.push_bind(record.key.to_owned());
                b.push_bind(record.code.to_owned());
                b.push_bind(record.created_at.to_owned());
                b.push_bind(record.updated_at.to_owned());
            });

            insert_result_int += query_builder
                .build()
                .execute(pool)
                .await
                .map_err(|e| anyhow::anyhow!(e))?
                .rows_affected();
        }

        Ok(insert_result_int)
    }

    pub async fn update_by_primary_key(
        pool: &Pool<MySql>,
        id: &i64,
        data: &OtpCodeRecord,
    ) -> Result<bool> {
        let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new("update t_otp_code ");

        query_builder.push("set f_user_id = ");
        query_builder.push_bind(data.user_id.trim());

        query_builder.push(", f_app = ");
        query_builder.push_bind(data.app.trim());

        query_builder.push(", f_name = ");
        query_builder.push_bind(data.name.trim());

        query_builder.push(", f_digits = ");
        query_builder.push_bind(data.digits);

        query_builder.push(", f_issuer = ");
        query_builder.push_bind(data.issuer.trim());

        query_builder.push(", f_period = ");
        query_builder.push_bind(data.period);

        query_builder.push(", f_expired_at = ");
        query_builder.push_bind(data.expired_at);

        query_builder.push(", f_key = ");
        query_builder.push_bind(data.key.trim());

        query_builder.push(", f_code = ");
        query_builder.push_bind(data.code);

        query_builder.push(", f_created_at = ");
        query_builder.push_bind(data.created_at);

        query_builder.push(", f_updated_at = ");
        query_builder.push_bind(data.updated_at);

        query_builder.push(" where f_id = ");
        query_builder.push_bind(id);

        let update_result_int = query_builder
            .build()
            .execute(pool)
            .await
            .map_err(|e| anyhow::anyhow!(e))?
            .rows_affected();

        Ok(update_result_int > 0)
    }

    pub async fn update_by_primary_key_selective(
            pool: &Pool<MySql>,
            id: &i64,
            data: &OtpCodeRecord,
        ) -> Result<bool> {
        let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new("update t_otp_code");


        query_builder.push("set f_user_id = ");
        query_builder.push_bind(data.user_id.trim());

        query_builder.push(", f_app = ");
        query_builder.push_bind(data.app.trim());

        query_builder.push(", f_name = ");
        query_builder.push_bind(data.name.trim());

        query_builder.push(", f_digits = ");
        query_builder.push_bind(data.digits);

        query_builder.push(", f_issuer = ");
        query_builder.push_bind(data.issuer.trim());

        query_builder.push(", f_period = ");
        query_builder.push_bind(data.period);

        query_builder.push(", f_expired_at = ");
        query_builder.push_bind(data.expired_at);

        query_builder.push(", f_key = ");
        query_builder.push_bind(data.key.trim());

        query_builder.push(", f_code = ");
        query_builder.push_bind(data.code);

        query_builder.push(", f_created_at = ");
        query_builder.push_bind(data.created_at);

        query_builder.push(", f_updated_at = ");
        query_builder.push_bind(data.updated_at);

        query_builder.push(" where f_id = ");
        query_builder.push_bind(id);

        let update_result_int = query_builder
            .build()
            .execute(pool)
            .await
            .map_err(|e| anyhow::anyhow!(e))?
            .rows_affected();

        Ok(update_result_int > 0)
    }

    pub fn from_row(row: sqlx::mysql::MySqlRow) -> Result<OtpCodeRecord> {
        Ok(OtpCodeRecord {
            id: row.get(0),
            user_id: row.get(1),
            app: row.get(2),
            name: row.get(3),
            digits: row.get(4),
            issuer: row.get(5),
            period: row.get(6),
            expired_at: row.get(7),
            key: row.get(8),
            code: row.get(9),
            created_at: row.get(10),
            updated_at: row.get(11),
        })
    }
}