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