Initial commit: git-issue-commit CLI tool
This commit is contained in:
96
src/fetcher/github.rs
Normal file
96
src/fetcher/github.rs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
use anyhow::{Result, Context};
|
||||||
|
use regex::Regex;
|
||||||
|
use reqwest::{Client, header::HeaderMap};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct GitHubIssueResponse {
|
||||||
|
number: u64,
|
||||||
|
title: String,
|
||||||
|
body: Option<String>,
|
||||||
|
html_url: String,
|
||||||
|
user: Option<GitHubUser>,
|
||||||
|
labels: Option<Vec<GitHubLabel>>,
|
||||||
|
state: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct GitHubUser {
|
||||||
|
login: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct GitHubLabel {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn fetch_github_issue(url: &str) -> Result<super::IssueContent> {
|
||||||
|
let (owner, repo, number) = parse_github_url(url)?;
|
||||||
|
|
||||||
|
let client = Client::new();
|
||||||
|
let token = env::var("GITHUB_TOKEN").ok();
|
||||||
|
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
if let Some(ref token) = token {
|
||||||
|
headers.insert("Authorization", format!("Bearer {}", token).parse()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
let api_url = env::var("GITHUB_API_URL").unwrap_or_else(|_| "https://api.github.com".to_string());
|
||||||
|
let issue_url = format!("{}/repos/{}/{}/issues/{}", api_url, owner, repo, number);
|
||||||
|
|
||||||
|
let response = client
|
||||||
|
.get(&issue_url)
|
||||||
|
.headers(headers)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.with_context(|| "Failed to fetch GitHub issue")?;
|
||||||
|
|
||||||
|
if !response.status().is_success() {
|
||||||
|
anyhow::bail!("Failed to fetch GitHub issue: HTTP {}", response.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
let issue: GitHubIssueResponse = response.json().await
|
||||||
|
.with_context(|| "Failed to parse GitHub response")?;
|
||||||
|
|
||||||
|
let labels = issue.labels.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.map(|l| l.name)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let author = issue.user.map(|u| u.login).unwrap_or_default();
|
||||||
|
|
||||||
|
Ok(super::IssueContent {
|
||||||
|
title: issue.title,
|
||||||
|
body: issue.body.unwrap_or_default(),
|
||||||
|
number: issue.number,
|
||||||
|
url: issue.html_url,
|
||||||
|
labels,
|
||||||
|
author,
|
||||||
|
state: issue.state.unwrap_or_default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_github_url(url: &str) -> Result<(String, String, u64)> {
|
||||||
|
let re = regex::Regex::new(r"github\.com/([a-zA-Z0-9._-]+)/([a-zA-Z0-9._-]+)(?:\.git)?/(?:issues|pull)/(\d+)")?;
|
||||||
|
let caps = re.captures(url)
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Invalid GitHub URL format: {}", url))?;
|
||||||
|
|
||||||
|
let owner = caps.get(1)
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Could not extract owner"))?
|
||||||
|
.as_str()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let repo = caps.get(2)
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Could not extract repository"))?
|
||||||
|
.as_str()
|
||||||
|
.trim_end_matches(".git")
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let number = caps.get(3)
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Could not extract issue number"))?
|
||||||
|
.as_str()
|
||||||
|
.parse::<u64>()?;
|
||||||
|
|
||||||
|
Ok((owner, repo, number))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user