trackWork 0.15.0

A terminal-based time tracking application for managing work sessions
pub mod jira;
pub mod secrets;

pub use secrets::SecretsManager;

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum IntegrationKind {
    CustomCommands,
    Jira,
}

impl Default for IntegrationKind {
    fn default() -> Self {
        IntegrationKind::CustomCommands
    }
}

impl IntegrationKind {
    pub fn display_name(&self) -> &'static str {
        match self {
            IntegrationKind::CustomCommands => "Custom Commands",
            IntegrationKind::Jira => "Jira Cloud",
        }
    }

    pub fn cycle_next(&self) -> Self {
        match self {
            IntegrationKind::CustomCommands => IntegrationKind::Jira,
            IntegrationKind::Jira => IntegrationKind::CustomCommands,
        }
    }
}

pub struct IntegrationOutput {
    pub success: bool,
    pub messages: Vec<String>,
    /// The Jira worklog id returned when logging work (None otherwise).
    pub worklog_id: Option<String>,
}

/// Dispatch log_work based on integration kind.
/// Returns None for CustomCommands (caller should fall through to shell logic).
pub fn log_work(
    kind: &IntegrationKind,
    jira_url: &str,
    jira_email: &str,
    api_token: &str,
    issue_key: &str,
    time_spent: &str,
    started: &str,
    description: &str,
) -> Option<IntegrationOutput> {
    match kind {
        IntegrationKind::CustomCommands => None,
        IntegrationKind::Jira => {
            Some(jira::log_work(jira_url, jira_email, api_token, issue_key, time_spent, started, description)
                .unwrap_or_else(|e| IntegrationOutput {
                    success: false,
                    messages: vec![format!("[JIRA] Error: {}", e)],
                    worklog_id: None,
                }))
        }
    }
}

/// Dispatch delete_work based on integration kind.
/// Returns None for CustomCommands (no remote worklog to delete).
pub fn delete_work(
    kind: &IntegrationKind,
    jira_url: &str,
    jira_email: &str,
    api_token: &str,
    issue_key: &str,
    worklog_id: &str,
) -> Option<IntegrationOutput> {
    match kind {
        IntegrationKind::CustomCommands => None,
        IntegrationKind::Jira => {
            Some(jira::delete_work(jira_url, jira_email, api_token, issue_key, worklog_id)
                .unwrap_or_else(|e| IntegrationOutput {
                    success: false,
                    messages: vec![format!("[JIRA] Error: {}", e)],
                    worklog_id: None,
                }))
        }
    }
}

/// Fetch a Jira issue summary (task name) by issue key.
/// Returns None for CustomCommands, Some(Result<String>) for Jira.
pub fn fetch_issue_summary(
    kind: &IntegrationKind,
    jira_url: &str,
    jira_email: &str,
    api_token: &str,
    issue_key: &str,
) -> Option<Result<String, String>> {
    match kind {
        IntegrationKind::CustomCommands => None,
        IntegrationKind::Jira => {
            Some(jira::fetch_issue_summary(jira_url, jira_email, api_token, issue_key)
                .map_err(|e| format!("[JIRA] Error fetching {}: {}", issue_key, e)))
        }
    }
}

/// Dispatch open_issue based on integration kind.
/// Returns None for CustomCommands (caller should fall through to shell logic).
pub fn open_issue(
    kind: &IntegrationKind,
    jira_url: &str,
    issue_key: &str,
) -> Option<IntegrationOutput> {
    match kind {
        IntegrationKind::CustomCommands => None,
        IntegrationKind::Jira => {
            Some(jira::open_issue(jira_url, issue_key)
                .unwrap_or_else(|e| IntegrationOutput {
                    success: false,
                    messages: vec![format!("[JIRA] Error: {}", e)],
                    worklog_id: None,
                }))
        }
    }
}