rust_commit_tracker/services/
database.rs

1use log::{debug, info};
2use sqlx::{Row, SqlitePool};
3use std::error::Error;
4
5pub struct Database {
6    pool: SqlitePool,
7}
8
9impl Database {
10    pub async fn new(database_url: &str) -> Result<Self, Box<dyn Error>> {
11        // Create the database file if it doesn't exist (for SQLite)
12        if database_url.starts_with("sqlite:") {
13            let file_path = database_url.strip_prefix("sqlite:").unwrap_or(database_url);
14            if !std::path::Path::new(file_path).exists() {
15                debug!("Creating SQLite database file: {}", file_path);
16                std::fs::File::create(file_path)?;
17            }
18        }
19
20        let pool = SqlitePool::connect(database_url).await?;
21
22        let db = Self { pool };
23        db.initialize().await?;
24
25        Ok(db)
26    }
27
28    async fn initialize(&self) -> Result<(), Box<dyn Error>> {
29        // Create the sent_commits table if it doesn't exist
30        sqlx::query(
31            r#"
32            CREATE TABLE IF NOT EXISTS sent_commits (
33                id INTEGER PRIMARY KEY,
34                commit_id INTEGER NOT NULL UNIQUE,
35                author TEXT NOT NULL,
36                message TEXT NOT NULL,
37                branch TEXT NOT NULL,
38                changeset TEXT NOT NULL,
39                sent_at DATETIME DEFAULT CURRENT_TIMESTAMP
40            )
41            "#,
42        )
43        .execute(&self.pool)
44        .await?;
45
46        debug!("Database initialized successfully");
47        Ok(())
48    }
49
50    pub async fn is_commit_sent(&self, commit_id: i32) -> Result<bool, Box<dyn Error>> {
51        let row = sqlx::query("SELECT COUNT(*) as count FROM sent_commits WHERE commit_id = ?")
52            .bind(commit_id)
53            .fetch_one(&self.pool)
54            .await?;
55
56        let count: i64 = row.get("count");
57        Ok(count > 0)
58    }
59
60    pub async fn mark_commit_sent(
61        &self,
62        commit_id: i32,
63        author: &str,
64        message: &str,
65        branch: &str,
66        changeset: &str,
67    ) -> Result<(), Box<dyn Error>> {
68        sqlx::query(
69            r#"
70            INSERT INTO sent_commits (commit_id, author, message, branch, changeset)
71            VALUES (?, ?, ?, ?, ?)
72            "#,
73        )
74        .bind(commit_id)
75        .bind(author)
76        .bind(message)
77        .bind(branch)
78        .bind(changeset)
79        .execute(&self.pool)
80        .await?;
81
82        debug!("Marked commit {} as sent", commit_id);
83        Ok(())
84    }
85
86    pub async fn get_last_sent_commit_id(&self) -> Result<Option<i32>, Box<dyn Error>> {
87        let row = sqlx::query("SELECT MAX(commit_id) as max_id FROM sent_commits")
88            .fetch_one(&self.pool)
89            .await?;
90
91        let max_id: Option<i32> = row.get("max_id");
92        Ok(max_id)
93    }
94
95    pub async fn get_last_sent_commit_info(&self) -> Result<Option<(i32, String)>, Box<dyn Error>> {
96        let row = sqlx::query(
97            "SELECT commit_id, changeset FROM sent_commits ORDER BY commit_id DESC LIMIT 1",
98        )
99        .fetch_optional(&self.pool)
100        .await?;
101
102        if let Some(row) = row {
103            let commit_id: i32 = row.get("commit_id");
104            let changeset: String = row.get("changeset");
105            Ok(Some((commit_id, changeset)))
106        } else {
107            Ok(None)
108        }
109    }
110
111    pub async fn get_sent_commits_count(&self) -> Result<i64, Box<dyn Error>> {
112        let row = sqlx::query("SELECT COUNT(*) as count FROM sent_commits")
113            .fetch_one(&self.pool)
114            .await?;
115
116        let count: i64 = row.get("count");
117        Ok(count)
118    }
119
120    pub async fn cleanup_old_commits(&self, keep_last: i64) -> Result<(), Box<dyn Error>> {
121        // Keep only the last N commits to prevent database from growing too large
122        sqlx::query(
123            r#"
124            DELETE FROM sent_commits 
125            WHERE id NOT IN (
126                SELECT id FROM sent_commits 
127                ORDER BY commit_id DESC 
128                LIMIT ?
129            )
130            "#,
131        )
132        .bind(keep_last)
133        .execute(&self.pool)
134        .await?;
135
136        info!("Cleaned up old commits, keeping last {}", keep_last);
137        Ok(())
138    }
139}