use anyhow::{Result, Context}; use regex::Regex; use reqwest::{Client, header::HeaderMap}; use serde::Deserialize; use std::env; #[derive(Deserialize)] struct GitLabIssueResponse { iid: u64, title: String, description: Option, web_url: String, author: Option, labels: Option>, state: Option, } #[derive(Deserialize)] struct GitLabAuthor { username: String, } pub async fn fetch_gitlab_issue(url: &str) -> Result { let (project_id, number) = parse_gitlab_url(url)?; let client = Client::new(); let token = env::var("GITLAB_TOKEN").ok(); let mut headers = HeaderMap::new(); if let Some(ref token) = token { headers.insert("PRIVATE-TOKEN", token.parse()?); } let api_url = env::var("GITLAB_API_URL").unwrap_or_else(|_| "https://gitlab.com/api/v4".to_string()); let project_encoded = project_id.replace("/", "%2F"); let issue_url = format!("{}/projects/{}/issues/{}", api_url, project_encoded, number); let response = client .get(&issue_url) .headers(headers) .send() .await .with_context(|| "Failed to fetch GitLab issue")?; if !response.status().is_success() { anyhow::bail!("Failed to fetch GitLab issue: HTTP {}", response.status()); } let issue: GitLabIssueResponse = response.json().await .with_context(|| "Failed to parse GitLab response")?; let labels = issue.labels.unwrap_or_default(); let author = issue.author.map(|a| a.username).unwrap_or_default(); Ok(super::IssueContent { title: issue.title, body: issue.description.unwrap_or_default(), number: issue.iid, url: issue.web_url, labels, author, state: issue.state.unwrap_or_default(), }) } fn parse_gitlab_url(url: &str) -> Result<(String, u64)> { let re = regex::Regex::new(r"gitlab\.com/([a-zA-Z0-9._/-]+)/-/?(?:issues|merge_requests)/(\d+)")?; let caps = re.captures(url) .ok_or_else(|| anyhow::anyhow!("Invalid GitLab URL format: {}", url))?; let project_id = caps.get(1) .ok_or_else(|| anyhow::anyhow!("Could not extract project ID"))? .as_str() .to_string(); let number = caps.get(2) .ok_or_else(|| anyhow::anyhow!("Could not extract issue number"))? .as_str() .parse::()?; Ok((project_id, number)) }