Initial commit: git-issue-commit CLI tool
Some checks failed
CI / test (push) Has been cancelled
CI / release (push) Has been cancelled

This commit is contained in:
2026-01-29 19:59:29 +00:00
parent cfa0e8879f
commit c09a918451

117
src/parser/url.rs Normal file
View File

@@ -0,0 +1,117 @@
use thiserror::Error;
#[derive(Debug, Clone, PartialEq)]
pub enum Provider {
GitHub,
GitLab,
}
#[derive(Debug, Clone)]
pub struct ParsedIssueUrl {
pub provider: Provider,
pub owner: String,
pub repo: String,
pub issue_number: u64,
pub is_pull_request: bool,
}
#[derive(Debug, Error, PartialEq)]
pub enum UrlParseError {
#[error("Invalid URL format")]
InvalidFormat,
#[error("Unsupported provider")]
UnsupportedProvider,
#[error("Missing issue number")]
MissingIssueNumber,
}
pub fn parse_issue_url(url: &str) -> Result<ParsedIssueUrl, UrlParseError> {
if let Some(github_url) = parse_github_url(url) {
return Ok(github_url);
}
if let Some(gitlab_url) = parse_gitlab_url(url) {
return Ok(gitlab_url);
}
Err(UrlParseError::InvalidFormat)
}
fn parse_github_url(url: &str) -> Option<ParsedIssueUrl> {
let re = regex::Regex::new(r"github\.com/([^/]+)/([^/]+)/(?:issues|pull)/(\d+)").expect("Invalid regex pattern for GitHub URLs");
let caps = re.captures(url)?;
let owner = caps.get(1)?.as_str().to_string();
let repo = caps.get(2)?.as_str().to_string();
let issue_number = caps.get(3)?.as_str().parse().ok()?;
let is_pull_request = url.contains("/pull/");
Some(ParsedIssueUrl {
provider: Provider::GitHub,
owner,
repo,
issue_number,
is_pull_request,
})
}
fn parse_gitlab_url(url: &str) -> Option<ParsedIssueUrl> {
let re = regex::Regex::new(r"gitlab\.com/([^/]+)/([^/]+)/-/issues/(\d+)").expect("Invalid regex pattern for GitLab URLs");
let caps = re.captures(url)?;
let owner = caps.get(1)?.as_str().to_string();
let repo = caps.get(2)?.as_str().to_string();
let issue_number = caps.get(3)?.as_str().parse().ok()?;
let is_pull_request = url.contains("/merge_requests");
Some(ParsedIssueUrl {
provider: Provider::GitLab,
owner,
repo,
issue_number,
is_pull_request,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_github_issue_url() {
let url = "https://github.com/owner/repo/issues/123";
let parsed = parse_issue_url(url).unwrap();
assert_eq!(parsed.provider, Provider::GitHub);
assert_eq!(parsed.owner, "owner");
assert_eq!(parsed.repo, "repo");
assert_eq!(parsed.issue_number, 123);
assert!(!parsed.is_pull_request);
}
#[test]
fn test_parse_github_pr_url() {
let url = "https://github.com/owner/repo/pull/456";
let parsed = parse_issue_url(url).unwrap();
assert_eq!(parsed.provider, Provider::GitHub);
assert_eq!(parsed.owner, "owner");
assert_eq!(parsed.repo, "repo");
assert_eq!(parsed.issue_number, 456);
assert!(parsed.is_pull_request);
}
#[test]
fn test_parse_gitlab_issue_url() {
let url = "https://gitlab.com/owner/repo/-/issues/789";
let parsed = parse_issue_url(url).unwrap();
assert_eq!(parsed.provider, Provider::GitLab);
assert_eq!(parsed.owner, "owner");
assert_eq!(parsed.repo, "repo");
assert_eq!(parsed.issue_number, 789);
assert!(!parsed.is_pull_request);
}
#[test]
fn test_parse_invalid_url() {
let result = parse_issue_url("not-a-valid-url");
assert!(result.is_err());
assert_eq!(result.unwrap_err(), UrlParseError::InvalidFormat);
}
}