anodizer_core/git/
remote.rs1use anyhow::Result;
2
3use super::git_output;
4
5pub(super) fn strip_url_credentials(url: &str) -> String {
11 if let Some(rest) = url.strip_prefix("https://")
12 && let Some(at_pos) = rest.find('@')
13 {
14 return format!("https://{}", &rest[at_pos + 1..]);
15 }
16 url.to_string()
17}
18
19pub fn parse_github_remote(url: &str) -> Option<(String, String)> {
22 let url = url.trim();
23 if url.is_empty() {
24 return None;
25 }
26
27 let url = url.strip_suffix(".git").unwrap_or(url);
29
30 if let Some(path) = url.strip_prefix("https://github.com/") {
32 let parts: Vec<&str> = path.splitn(3, '/').collect();
33 if parts.len() >= 2 && !parts[0].is_empty() && !parts[1].is_empty() {
34 return Some((parts[0].to_string(), parts[1].to_string()));
35 }
36 }
37
38 if let Some(path) = url.strip_prefix("git@github.com:") {
40 let parts: Vec<&str> = path.splitn(3, '/').collect();
41 if parts.len() >= 2 && !parts[0].is_empty() && !parts[1].is_empty() {
42 return Some((parts[0].to_string(), parts[1].to_string()));
43 }
44 }
45
46 None
47}
48
49pub fn detect_github_repo() -> Result<(String, String)> {
51 let url = git_output(&["remote", "get-url", "origin"])?;
52 parse_github_remote(&url).ok_or_else(|| {
53 let safe = strip_url_credentials(&url);
56 anyhow::anyhow!(
57 "could not parse GitHub owner/repo from remote URL: {}",
58 safe
59 )
60 })
61}
62
63pub fn parse_remote_owner_repo(url: &str) -> Option<(String, String)> {
71 let url = url.trim();
72 if url.is_empty() {
73 return None;
74 }
75
76 let url = url.strip_suffix(".git").unwrap_or(url);
78
79 if url.starts_with("https://") || url.starts_with("http://") {
81 let after_scheme = if let Some(rest) = url.strip_prefix("https://") {
83 rest
84 } else {
85 url.strip_prefix("http://")?
86 };
87 let after_host = after_scheme.find('/').map(|i| &after_scheme[i + 1..])?;
89 let last_slash = after_host.rfind('/')?;
92 let owner = &after_host[..last_slash];
93 let repo = &after_host[last_slash + 1..];
94 if !owner.is_empty() && !repo.is_empty() {
95 return Some((owner.to_string(), repo.to_string()));
96 }
97 }
98
99 if let Some(colon_pos) = url.find(':') {
101 let before_colon = &url[..colon_pos];
102 if before_colon.contains('@') && !before_colon.contains("//") {
104 let path = &url[colon_pos + 1..];
105 let last_slash = path.rfind('/')?;
106 let owner = &path[..last_slash];
107 let repo = &path[last_slash + 1..];
108 if !owner.is_empty() && !repo.is_empty() {
109 return Some((owner.to_string(), repo.to_string()));
110 }
111 }
112 }
113
114 None
115}
116
117pub fn detect_owner_repo() -> Result<(String, String)> {
122 let url = git_output(&["remote", "get-url", "origin"])?;
123 parse_remote_owner_repo(&url).ok_or_else(|| {
124 let safe = strip_url_credentials(&url);
126 anyhow::anyhow!("could not parse owner/repo from remote URL: {}", safe)
127 })
128}