wsb 0.183.4039

Workspace - A tool suite for file operations, version management, and development workflow automation
Documentation
use crate::prelude::*;
use r2d2::PooledConnection;
use r2d2_sqlite::SqliteConnectionManager;
use rusqlite::{params, Row};

use crate::persist::local::models::schema_models::Feature;

pub type DbConn = PooledConnection<SqliteConnectionManager>;

#[derive(Clone, Debug)]
pub struct FeatureRepo {
    db: Arc<DatabaseService>,
}

impl FeatureRepo {
    pub fn new(db: Arc<DatabaseService>) -> Self {
        Self { db }
    }
}

impl Repository<Feature> for FeatureRepo {
    fn get_connection(&self) -> Result<r2d2::PooledConnection<SqliteConnectionManager>> {
        self.db.get_connection().map_err(Into::into)
    }

    fn map_row(row: &Row) -> Result<Feature> {
        let created_at_str: String = row
            .get("created_at")
            .map_err(|e| anyhow::anyhow!("{}", e))?;
        let updated_at_str: String = row
            .get("updated_at")
            .map_err(|e| anyhow::anyhow!("{}", e))?;
        let created_at = parse_timestamp(&created_at_str)?;
        let updated_at = parse_timestamp(&updated_at_str)?;

        Ok(Feature::from_db_row(
            row.get("id").map_err(|e| anyhow::anyhow!("{}", e))?,
            row.get("project_id")
                .map_err(|e| anyhow::anyhow!("{}", e))?,
            row.get("code").map_err(|e| anyhow::anyhow!("{}", e))?,
            row.get("name").map_err(|e| anyhow::anyhow!("{}", e))?,
            row.get("description")
                .map_err(|e| anyhow::anyhow!("{}", e))?,
            row.get("category").map_err(|e| anyhow::anyhow!("{}", e))?,
            row.get("state").map_err(|e| anyhow::anyhow!("{}", e))?,
            row.get("test_status")
                .map_err(|e| anyhow::anyhow!("{}", e))?,
            row.get("priority").map_err(|e| anyhow::anyhow!("{}", e))?,
            row.get("notes").map_err(|e| anyhow::anyhow!("{}", e))?,
            created_at,
            updated_at,
        )
        .map_err(|e| anyhow::anyhow!("{}", e))?)
    }

    fn create_impl(
        &self,
        entity: &Feature,
        conn: &PooledConnection<SqliteConnectionManager>,
    ) -> Result<()> {
        conn.execute(
            r#"
            INSERT INTO features (id, project_id, code, name, description, category, state, test_status, priority, notes, created_at, updated_at)
            VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)
            "#,
            params![
                &entity.id,
                &entity.project_id,
                &entity.code,
                &entity.name,
                &entity.description,
                &entity.category,
                &entity.state,
                &entity.test_status,
                &entity.priority,
                &entity.notes,
                &entity.created_at.to_rfc3339(),
                &entity.updated_at.to_rfc3339(),
            ],
        ).map_err(|e| anyhow::anyhow!("{}", e))?;
        Ok(())
    }

    fn find_by_id_impl(
        &self,
        id: &str,
        conn: &PooledConnection<SqliteConnectionManager>,
    ) -> Result<Option<Feature>> {
        let mut stmt = conn.prepare(
            "SELECT id, project_id, code, name, description, category, state, test_status, priority, notes, created_at, updated_at FROM features WHERE id = ?1",
        ).map_err(|e| anyhow::anyhow!("{}", e))?;
        let mut rows = stmt
            .query(params![id])
            .map_err(|e| anyhow::anyhow!("{}", e))?;
        if let Some(row) = rows.next().map_err(|e| anyhow::anyhow!("{}", e))? {
            Ok(Some(Self::map_row(row)?))
        } else {
            Ok(None)
        }
    }

    fn update_impl(
        &self,
        entity: &Feature,
        conn: &PooledConnection<SqliteConnectionManager>,
    ) -> Result<()> {
        conn.execute(
            r#"
            UPDATE features SET name = ?1, description = ?2, state = ?3, test_status = ?4, priority = ?5, notes = ?6, updated_at = ?7
            WHERE id = ?8
            "#,
            params![
                &entity.name,
                &entity.description,
                &entity.state,
                &entity.test_status,
                &entity.priority,
                &entity.notes,
                chrono::Utc::now().to_rfc3339(),
                &entity.id,
            ],
        ).map_err(|e| anyhow::anyhow!("{}", e))?;
        Ok(())
    }

    fn upsert_impl(
        &self,
        entity: &Feature,
        conn: &PooledConnection<SqliteConnectionManager>,
    ) -> Result<()> {
        conn.execute(
            r#"
            INSERT OR REPLACE INTO features (id, project_id, code, name, description, category, state, test_status, priority, notes, created_at, updated_at)
            VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)
            "#,
            params![
                &entity.id,
                &entity.project_id,
                &entity.code,
                &entity.name,
                &entity.description,
                &entity.category,
                &entity.state,
                &entity.test_status,
                &entity.priority,
                &entity.notes,
                &entity.created_at.to_rfc3339(),
                &entity.updated_at.to_rfc3339(),
            ],
        ).map_err(|e| anyhow::anyhow!("{}", e))?;
        Ok(())
    }

    fn delete_impl(
        &self,
        id: &str,
        conn: &PooledConnection<SqliteConnectionManager>,
    ) -> Result<()> {
        conn.execute("DELETE FROM features WHERE id = ?", params![id])
            .map_err(|e| anyhow::anyhow!("{}", e))?;
        Ok(())
    }

    fn list_impl(
        &self,
        conn: &PooledConnection<SqliteConnectionManager>,
        _offset: Option<i64>,
        _limit: Option<i64>,
    ) -> Result<Vec<Feature>> {
        let mut stmt = conn.prepare(
            "SELECT id, project_id, code, name, description, category, state, test_status, priority, notes, created_at, updated_at FROM features ORDER BY created_at DESC",
        ).map_err(|e| anyhow::anyhow!("{}", e))?;
        let mut rows = stmt.query([]).map_err(|e| anyhow::anyhow!("{}", e))?;
        let mut features = Vec::new();
        while let Some(row) = rows.next().map_err(|e| anyhow::anyhow!("{}", e))? {
            features.push(Self::map_row(row)?);
        }
        Ok(features)
    }

    fn count_impl(&self, conn: &PooledConnection<SqliteConnectionManager>) -> Result<i64> {
        let count: i64 = conn
            .query_row("SELECT COUNT(*) FROM features", [], |row| row.get(0))
            .map_err(|e| anyhow::anyhow!("{}", e))?;
        Ok(count)
    }
}

fn parse_timestamp(s: &str) -> Result<chrono::DateTime<chrono::Utc>> {
    if let Ok(parsed) = chrono::DateTime::parse_from_rfc3339(s) {
        Ok(parsed.with_timezone(&chrono::Utc))
    } else {
        let parsed = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S")?;
        Ok(parsed.and_utc())
    }
}