ruskit 0.1.5

A modern web framework for Rust inspired by Laravel
Documentation
use sqlx::{FromRow, Row, sqlite::SqliteRow};
use crate::framework::database::{get_pool, DatabaseError};

pub struct QueryBuilder {
    table: String,
    conditions: Vec<String>,
    bindings: Vec<String>,
    select: Option<String>,
    limit: Option<i64>,
    offset: Option<i64>,
    order_by: Option<(String, String)>,
}

impl QueryBuilder {
    pub fn table(table: &str) -> Self {
        Self {
            table: table.to_string(),
            conditions: Vec::new(),
            bindings: Vec::new(),
            select: None,
            limit: None,
            offset: None,
            order_by: None,
        }
    }

    pub fn select(mut self, columns: &str) -> Self {
        self.select = Some(columns.to_string());
        self
    }

    pub fn where_clause(mut self, column: &str, operator: &str, value: impl ToString) -> Self {
        self.conditions.push(format!("{} {} ?", column, operator));
        self.bindings.push(value.to_string());
        self
    }

    pub fn limit(mut self, limit: i64) -> Self {
        self.limit = Some(limit);
        self
    }

    pub fn offset(mut self, offset: i64) -> Self {
        self.offset = Some(offset);
        self
    }

    pub fn order_by(mut self, column: &str, direction: &str) -> Self {
        self.order_by = Some((column.to_string(), direction.to_string()));
        self
    }

    pub async fn get<T>(self) -> Result<Vec<T>, DatabaseError> 
    where
        T: for<'r> FromRow<'r, SqliteRow> + Send + Unpin,
    {
        let pool = get_pool()?;
        let mut query = format!(
            "SELECT {} FROM {}",
            self.select.unwrap_or_else(|| "*".to_string()),
            self.table
        );

        if !self.conditions.is_empty() {
            query.push_str(" WHERE ");
            query.push_str(&self.conditions.join(" AND "));
        }

        if let Some((column, direction)) = self.order_by {
            query.push_str(&format!(" ORDER BY {} {}", column, direction));
        }

        if let Some(limit) = self.limit {
            query.push_str(&format!(" LIMIT {}", limit));
        }

        if let Some(offset) = self.offset {
            query.push_str(&format!(" OFFSET {}", offset));
        }

        let mut query_builder = sqlx::query_as(&query);
        for binding in self.bindings {
            query_builder = query_builder.bind(binding);
        }

        let results = query_builder.fetch_all(pool.as_ref()).await?;
        Ok(results)
    }

    pub async fn first<T>(self) -> Result<Option<T>, DatabaseError>
    where
        T: for<'r> FromRow<'r, SqliteRow> + Send + Unpin,
    {
        let results = self.limit(1).get::<T>().await?;
        Ok(results.into_iter().next())
    }

    pub async fn count(self) -> Result<i64, DatabaseError> {
        let pool = get_pool()?;
        let mut query = format!("SELECT COUNT(*) as count FROM {}", self.table);

        if !self.conditions.is_empty() {
            query.push_str(" WHERE ");
            query.push_str(&self.conditions.join(" AND "));
        }

        let mut query_builder = sqlx::query(&query);
        for binding in self.bindings {
            query_builder = query_builder.bind(binding);
        }

        let result = query_builder
            .fetch_one(pool.as_ref())
            .await?
            .try_get::<i64, _>(0)?;

        Ok(result)
    }
}