use crate::prelude::*;
use r2d2::PooledConnection;
use r2d2_sqlite::SqliteConnectionManager;
use rusqlite::{params, Row};
use crate::persist::local::models::schema_models::Session;
pub type DbConn = PooledConnection<SqliteConnectionManager>;
#[derive(Clone, Debug)]
pub struct LocalSessionRepo {
db: Arc<DatabaseService>,
}
impl LocalSessionRepo {
pub fn new(db: Arc<DatabaseService>) -> Self {
Self { db }
}
}
impl Repository<Session> for LocalSessionRepo {
fn get_connection(&self) -> Result<r2d2::PooledConnection<SqliteConnectionManager>> {
self.db.get_connection().map_err(Into::into)
}
fn map_row(row: &Row) -> Result<Session> {
let started_at_str: String = row
.get("started_at")
.map_err(|e| anyhow::anyhow!("{}", e))?;
let updated_at_str: String = row
.get("updated_at")
.map_err(|e| anyhow::anyhow!("{}", e))?;
let started_at = parse_timestamp(&started_at_str)?;
let updated_at = parse_timestamp(&updated_at_str)?;
Ok(Session::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("title").map_err(|e| anyhow::anyhow!("{}", e))?,
row.get("date").map_err(|e| anyhow::anyhow!("{}", e))?,
row.get("start_time")
.map_err(|e| anyhow::anyhow!("{}", e))?,
row.get("end_time").map_err(|e| anyhow::anyhow!("{}", e))?,
row.get("state").map_err(|e| anyhow::anyhow!("{}", e))?,
row.get("focus").map_err(|e| anyhow::anyhow!("{}", e))?,
row.get("major_achievement")
.map_err(|e| anyhow::anyhow!("{}", e))?,
row.get("completed_tasks")
.map_err(|e| anyhow::anyhow!("{}", e))?,
row.get("key_achievements")
.map_err(|e| anyhow::anyhow!("{}", e))?,
row.get("files_modified")
.map_err(|e| anyhow::anyhow!("{}", e))?,
row.get("issues_resolved")
.map_err(|e| anyhow::anyhow!("{}", e))?,
started_at,
updated_at,
)
.map_err(|e| anyhow::anyhow!("{}", e))?)
}
fn create_impl(
&self,
entity: &Session,
conn: &PooledConnection<SqliteConnectionManager>,
) -> Result<()> {
conn.execute(
r#"
INSERT INTO work_sessions (id, project_id, code, title, date, start_time, end_time, state, focus, major_achievement, completed_tasks, key_achievements, files_modified, issues_resolved, started_at, updated_at)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16)
"#,
params![
&entity.id,
&entity.project_id,
&entity.code,
&entity.title,
&entity.date,
&entity.start_time,
&entity.end_time,
&entity.status,
&entity.focus,
&entity.major_achievement,
&entity.completed_tasks,
&entity.key_achievements,
&entity.files_modified,
&entity.issues_resolved,
&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<Session>> {
let mut stmt = conn.prepare(
"SELECT id, project_id, code, title, date, start_time, end_time, state, focus, major_achievement, completed_tasks, key_achievements, files_modified, issues_resolved, started_at, updated_at FROM work_sessions 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: &Session,
conn: &PooledConnection<SqliteConnectionManager>,
) -> Result<()> {
conn.execute(
r#"
UPDATE work_sessions SET title = ?1, focus = ?2, major_achievement = ?3, completed_tasks = ?4, key_achievements = ?5, files_modified = ?6, issues_resolved = ?7, state = ?8, end_time = ?9, updated_at = ?10
WHERE id = ?11
"#,
params![
&entity.title,
&entity.focus,
&entity.major_achievement,
&entity.completed_tasks,
&entity.key_achievements,
&entity.files_modified,
&entity.issues_resolved,
&entity.status,
&entity.end_time,
chrono::Utc::now().to_rfc3339(),
&entity.id,
],
).map_err(|e| anyhow::anyhow!("{}", e))?;
Ok(())
}
fn upsert_impl(
&self,
entity: &Session,
conn: &PooledConnection<SqliteConnectionManager>,
) -> Result<()> {
conn.execute(
r#"
INSERT OR REPLACE INTO work_sessions (id, project_id, code, title, date, start_time, end_time, state, focus, major_achievement, completed_tasks, key_achievements, files_modified, issues_resolved, started_at, updated_at)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16)
"#,
params![
&entity.id,
&entity.project_id,
&entity.code,
&entity.title,
&entity.date,
&entity.start_time,
&entity.end_time,
&entity.status,
&entity.focus,
&entity.major_achievement,
&entity.completed_tasks,
&entity.key_achievements,
&entity.files_modified,
&entity.issues_resolved,
&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 work_sessions 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<Session>> {
let mut stmt = conn.prepare(
"SELECT id, project_id, code, title, date, start_time, end_time, state, focus, major_achievement, completed_tasks, key_achievements, files_modified, issues_resolved, started_at, updated_at FROM work_sessions ORDER BY started_at DESC",
).map_err(|e| anyhow::anyhow!("{}", e))?;
let mut rows = stmt.query([]).map_err(|e| anyhow::anyhow!("{}", e))?;
let mut sessions = Vec::new();
while let Some(row) = rows.next().map_err(|e| anyhow::anyhow!("{}", e))? {
sessions.push(Self::map_row(row)?);
}
Ok(sessions)
}
fn count_impl(&self, conn: &PooledConnection<SqliteConnectionManager>) -> Result<i64> {
let count: i64 = conn
.query_row("SELECT COUNT(*) FROM work_sessions", [], |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())
}
}