1use git2::Reference;
2use git2::Repository;
3use rootcause::bail;
4use rootcause::prelude::ResultExt;
5use rootcause::report;
6
7pub fn get_default(repo: &Repository) -> rootcause::Result<Reference<'_>> {
16 for remote_name in repo.remotes()?.iter().flatten() {
17 if let Ok(default_remote_ref) = repo.find_reference(&format!("refs/remotes/{remote_name}/HEAD")) {
18 return Ok(default_remote_ref);
19 }
20 }
21 bail!("error missing default remote")
22}
23
24pub fn get_https_urls(repo: &Repository) -> rootcause::Result<Vec<String>> {
32 let mut https_urls = vec![];
33 for remote_name in repo.remotes()?.iter().flatten() {
34 let remote = repo.find_remote(remote_name)?;
35 let url = remote
36 .url()
37 .ok_or_else(|| report!("error invalid URL for remote"))
38 .attach_with(|| format!("remote={remote_name:?}"))
39 .and_then(map_to_https_url)?;
40 https_urls.push(url);
41 }
42 Ok(https_urls)
43}
44
45pub enum GitProvider {
47 GitHub,
49 GitLab,
51}
52
53impl GitProvider {
54 pub fn get(url: &str) -> rootcause::Result<Option<Self>> {
63 let resp = ureq::get(url).call()?;
64
65 for (name, _) in resp.headers() {
66 let name = name.as_str();
67 if name.contains("gitlab") {
68 return Ok(Some(Self::GitLab));
69 }
70 if name.contains("github") {
71 return Ok(Some(Self::GitHub));
72 }
73 }
74
75 Ok(None)
76 }
77}
78
79fn map_to_https_url(url: &str) -> rootcause::Result<String> {
80 if url.starts_with("https://") {
81 return Ok(url.to_owned());
82 }
83 if let Some(rest) = url
84 .strip_prefix("ssh://")
85 .and_then(|no_ssh| no_ssh.strip_prefix("git@"))
86 .or_else(|| url.strip_prefix("git@"))
87 {
88 return Ok(format!("https://{}", rest.replace(':', "/").trim_end_matches(".git")));
89 }
90 Err(report!("error unsupported protocol for URL").attach(format!("url={url}")))
91}
92
93#[cfg(test)]
94mod tests {
95 use rstest::rstest;
96
97 use super::*;
98
99 #[rstest]
100 #[case("https://github.com/user/repo", "https://github.com/user/repo")]
101 #[case("https://gitlab.com/user/repo", "https://gitlab.com/user/repo")]
102 #[case("git@github.com:user/repo.git", "https://github.com/user/repo")]
103 #[case("ssh://git@github.com/user/repo.git", "https://github.com/user/repo")]
104 #[case("git@gitlab.com:user/repo.git", "https://gitlab.com/user/repo")]
105 #[case("https://bitbucket.org/user/repo", "https://bitbucket.org/user/repo")]
106 fn map_to_https_url_when_valid_input_maps_successfully(#[case] input: &str, #[case] expected: &str) {
107 let result = map_to_https_url(input);
108 assert2::assert!(let Ok(actual) = result);
109 pretty_assertions::assert_eq!(actual, expected);
110 }
111
112 #[rstest]
113 #[case("ftp://example.com/repo")]
114 #[case("http://github.com/user/repo")]
115 #[case("invalid")]
116 #[case("")]
117 fn map_to_https_url_when_unsupported_protocol_returns_error(#[case] input: &str) {
118 let result = map_to_https_url(input);
119 assert2::assert!(let Err(err) = result);
120 assert!(err.to_string().contains("error unsupported protocol for URL"));
121 }
122}