rust_commit_tracker/services/
database.rs1use 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 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 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 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}