anodizer_core/git/
remote.rs1use anyhow::Result;
2
3use super::git_output;
4use crate::redact::redact_url_credentials;
5
6pub fn parse_github_remote(url: &str) -> Option<(String, String)> {
9 let url = url.trim();
10 if url.is_empty() {
11 return None;
12 }
13
14 let url = url.strip_suffix(".git").unwrap_or(url);
16
17 if let Some(path) = url.strip_prefix("https://github.com/") {
19 let parts: Vec<&str> = path.splitn(3, '/').collect();
20 if parts.len() >= 2 && !parts[0].is_empty() && !parts[1].is_empty() {
21 return Some((parts[0].to_string(), parts[1].to_string()));
22 }
23 }
24
25 if let Some(path) = url.strip_prefix("git@github.com:") {
27 let parts: Vec<&str> = path.splitn(3, '/').collect();
28 if parts.len() >= 2 && !parts[0].is_empty() && !parts[1].is_empty() {
29 return Some((parts[0].to_string(), parts[1].to_string()));
30 }
31 }
32
33 None
34}
35
36pub fn detect_github_repo() -> Result<(String, String)> {
38 let url = git_output(&["remote", "get-url", "origin"])?;
39 parse_github_remote(&url).ok_or_else(|| {
40 let safe = redact_url_credentials(&url);
43 anyhow::anyhow!(
44 "could not parse GitHub owner/repo from remote URL: {}",
45 safe
46 )
47 })
48}
49
50pub fn parse_remote_owner_repo(url: &str) -> Option<(String, String)> {
58 let url = url.trim();
59 if url.is_empty() {
60 return None;
61 }
62
63 let url = url.strip_suffix(".git").unwrap_or(url);
65
66 if url.starts_with("https://") || url.starts_with("http://") {
68 let after_scheme = if let Some(rest) = url.strip_prefix("https://") {
70 rest
71 } else {
72 url.strip_prefix("http://")?
73 };
74 let after_host = after_scheme.find('/').map(|i| &after_scheme[i + 1..])?;
76 let last_slash = after_host.rfind('/')?;
79 let owner = &after_host[..last_slash];
80 let repo = &after_host[last_slash + 1..];
81 if !owner.is_empty() && !repo.is_empty() {
82 return Some((owner.to_string(), repo.to_string()));
83 }
84 }
85
86 if let Some(colon_pos) = url.find(':') {
88 let before_colon = &url[..colon_pos];
89 if before_colon.contains('@') && !before_colon.contains("//") {
91 let path = &url[colon_pos + 1..];
92 let last_slash = path.rfind('/')?;
93 let owner = &path[..last_slash];
94 let repo = &path[last_slash + 1..];
95 if !owner.is_empty() && !repo.is_empty() {
96 return Some((owner.to_string(), repo.to_string()));
97 }
98 }
99 }
100
101 None
102}
103
104pub fn detect_owner_repo() -> Result<(String, String)> {
109 let url = git_output(&["remote", "get-url", "origin"])?;
110 parse_remote_owner_repo(&url).ok_or_else(|| {
111 let safe = redact_url_credentials(&url);
113 anyhow::anyhow!("could not parse owner/repo from remote URL: {}", safe)
114 })
115}