diff --git a/src/fetcher/gitlab.rs b/src/fetcher/gitlab.rs new file mode 100644 index 0000000..5453f3b --- /dev/null +++ b/src/fetcher/gitlab.rs @@ -0,0 +1,82 @@ +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)) +}