pub mod github_issues;
pub mod jira;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use crate::config::{ParsecConfig, TrackerProvider};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Ticket {
pub id: String,
pub title: String,
pub status: Option<String>,
pub assignee: Option<String>,
pub url: Option<String>,
}
fn load_atlassian_env() {
let env_path: PathBuf = dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".claude")
.join(".atlassian-env");
if let Ok(contents) = std::fs::read_to_string(&env_path) {
for line in contents.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
if let Some((key, value)) = line.split_once('=') {
let key = key.trim();
let value = value.trim();
if std::env::var(key).is_err() {
std::env::set_var(key, value);
}
}
}
}
}
pub async fn fetch_ticket(
config: &ParsecConfig,
id: &str,
repo_root: Option<&Path>,
) -> Result<Option<Ticket>> {
load_atlassian_env();
match config.tracker.provider {
TrackerProvider::Jira => fetch_jira_ticket(config, id).await,
TrackerProvider::Github => {
let tracker = github_issues::GithubIssueTracker::new(repo_root);
let ticket = tracker.fetch_ticket(id).await?;
Ok(Some(ticket))
}
TrackerProvider::None => {
if std::env::var("JIRA_BASE_URL").is_ok()
&& (std::env::var("JIRA_PAT").is_ok() || std::env::var("PARSEC_JIRA_TOKEN").is_ok())
{
if let Ok(Some(ticket)) = fetch_jira_ticket(config, id).await {
return Ok(Some(ticket));
}
}
if let Some(root) = repo_root {
if let Ok(remote_url) = crate::git::get_remote_url(root) {
if crate::github::parse_github_remote(&remote_url).is_some() {
let clean_id = id.trim_start_matches('#');
if clean_id.chars().all(|c| c.is_ascii_digit()) {
let tracker = github_issues::GithubIssueTracker::new(repo_root);
if let Ok(ticket) = tracker.fetch_ticket(id).await {
return Ok(Some(ticket));
}
}
}
}
}
Ok(None)
}
}
}
async fn fetch_jira_ticket(config: &ParsecConfig, id: &str) -> Result<Option<Ticket>> {
let base_url = config
.tracker
.jira
.as_ref()
.map(|j| j.base_url.clone())
.or_else(|| std::env::var("JIRA_BASE_URL").ok())
.ok_or_else(|| {
anyhow::anyhow!("Jira base URL not found. Set it in config or JIRA_BASE_URL env var.")
})?;
let email = config.tracker.jira.as_ref().and_then(|j| j.email.clone());
let tracker = jira::JiraTracker::new(&base_url, email.as_deref());
let ticket = tracker.fetch_ticket(id).await?;
Ok(Some(ticket))
}