trackWork 0.15.0

A terminal-based time tracking application for managing work sessions
use anyhow::Result;
use rusqlite::params;

use crate::db::Database;
use crate::models::Task;

impl Database {
    pub fn get_all_tasks(&self) -> Result<Vec<Task>> {
        let mut stmt = self.conn.prepare(
            "SELECT id, issue_key, name, project FROM tasks ORDER BY project, issue_key"
        )?;

        let tasks = stmt.query_map([], |row| {
            Ok(Task {
                id: row.get(0)?,
                issue_key: row.get(1)?,
                name: row.get(2)?,
                project: row.get(3)?,
            })
        })?
        .collect::<Result<Vec<_>, _>>()?;

        Ok(tasks)
    }

    pub fn create_task(&self, issue_key: &str, name: &str, project: &str) -> Result<i64> {
        self.conn.execute(
            "INSERT INTO tasks (issue_key, name, project) VALUES (?1, ?2, ?3)",
            params![issue_key, name, project],
        )?;
        Ok(self.conn.last_insert_rowid())
    }

    pub fn update_task(&self, id: i64, issue_key: &str, name: &str, project: &str) -> Result<()> {
        self.conn.execute(
            "UPDATE tasks SET issue_key = ?1, name = ?2, project = ?3 WHERE id = ?4",
            params![issue_key, name, project, id],
        )?;
        Ok(())
    }

    pub fn delete_task(&self, id: i64) -> Result<()> {
        self.conn.execute("DELETE FROM tasks WHERE id = ?1", params![id])?;
        Ok(())
    }

    pub fn get_task_by_issue_key(&self, issue_key: &str) -> Result<Option<Task>> {
        let mut stmt = self.conn.prepare(
            "SELECT id, issue_key, name, project FROM tasks WHERE issue_key = ?1"
        )?;
        let mut rows = stmt.query_map(params![issue_key], |row| {
            Ok(Task {
                id: row.get(0)?,
                issue_key: row.get(1)?,
                name: row.get(2)?,
                project: row.get(3)?,
            })
        })?;
        match rows.next() {
            Some(Ok(task)) => Ok(Some(task)),
            Some(Err(e)) => Err(e.into()),
            None => Ok(None),
        }
    }

    pub fn update_task_name(&self, issue_key: &str, name: &str) -> Result<()> {
        self.conn.execute(
            "UPDATE tasks SET name = ?1 WHERE issue_key = ?2",
            params![name, issue_key],
        )?;
        Ok(())
    }

    pub fn get_task_name_map(&self) -> Result<std::collections::HashMap<String, String>> {
        let mut stmt = self.conn.prepare(
            "SELECT issue_key, name FROM tasks WHERE name != ''"
        )?;
        let map = stmt.query_map([], |row| {
            Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?))
        })?
        .filter_map(|r| r.ok())
        .collect();
        Ok(map)
    }

    pub fn get_tasks_with_empty_names(&self) -> Result<Vec<String>> {
        let mut stmt = self.conn.prepare(
            "SELECT issue_key FROM tasks WHERE name = '' AND issue_key != ''"
        )?;
        let keys = stmt.query_map([], |row| row.get::<_, String>(0))?
            .filter_map(|r| r.ok())
            .collect();
        Ok(keys)
    }

    pub fn ensure_task_exists(&self, issue_key: &str) -> Result<()> {
        self.conn.execute(
            "INSERT OR IGNORE INTO tasks (issue_key) VALUES (?1)",
            params![issue_key],
        )?;
        Ok(())
    }
}