1use crate::Result;
2use git2::{ErrorClass, ErrorCode};
3use log::debug;
4use std::path::Path;
5
6#[derive(PartialEq, Debug, Default)]
7pub struct GitUrlComponents {
8 pub domain: String,
9 pub username: String,
10 pub repo: String,
11 pub suffix: String,
12}
13
14#[must_use]
15pub fn parse_git_url_https(http_url: &str) -> Option<GitUrlComponents> {
16 let mut split: Vec<_> = http_url.split('/').collect();
17
18 while let Some(&"") = split.last() {
19 split.pop();
20 }
21 if split.len() != 5 {
22 return None;
23 }
24 if split[0] != "https:" && split[0] != "http:" {
25 return None;
26 }
27 let domain = split[2];
28 let username = split[3];
29 let repo = split[4];
30 let suffix = match domain {
31 "git.sr.ht" => "",
32 "github.com" | "gitlab.com" => {
33 if repo.ends_with(".git") {
34 ""
35 } else {
36 ".git"
37 }
38 }
39 _ => return None,
40 };
41
42 Some(GitUrlComponents {
43 domain: domain.to_string(),
44 username: username.to_string(),
45 repo: repo.to_string(),
46 suffix: suffix.to_string(),
47 })
48}
49
50#[must_use]
51pub fn is_unrecoverable(err: &git2::Error) -> bool {
52 matches!(
53 (err.class(), err.code()),
54 (ErrorClass::Http, ErrorCode::Auth) |
56 (ErrorClass::Repository, ErrorCode::NotFound) |
57 (ErrorClass::Reference, ErrorCode::GenericError)
59 )
60}
61
62pub fn fetch_and_checkout_git_repo(repo: &git2::Repository) -> Result<(), git2::Error> {
63 let mut fetch_options = default_fetch_options();
64 repo.find_remote("origin")?
65 .fetch::<String>(&[], Some(&mut fetch_options), None)?;
66 repo.set_head("FETCH_HEAD")?;
67 let mut opts = git2::build::CheckoutBuilder::new();
68 opts.force();
69 repo.checkout_head(Some(&mut opts))
70}
71
72pub fn clone<P: AsRef<Path>>(
74 url: &str,
75 path: P,
76) -> std::result::Result<git2::Repository, git2::Error> {
77 debug!("Cloning {} to {}", url, path.as_ref().display());
78 let fetch_options = default_fetch_options();
79 git2::build::RepoBuilder::new()
80 .fetch_options(fetch_options)
81 .clone(url, path.as_ref())
82}
83
84#[must_use]
88pub fn default_fetch_options<'a>() -> git2::FetchOptions<'a> {
89 let mut proxy_options = git2::ProxyOptions::new();
91 proxy_options.auto();
92 let mut fetch_options = git2::FetchOptions::new();
93 fetch_options.proxy_options(proxy_options);
94
95 fetch_options
96}
97
98#[test]
99fn parse_git_url_https_test() {
100 assert_eq!(
101 parse_git_url_https("https://github.com/dpc/trust"),
102 Some(GitUrlComponents {
103 domain: "github.com".to_string(),
104 username: "dpc".to_string(),
105 repo: "trust".to_string(),
106 suffix: ".git".to_string()
107 })
108 );
109 assert_eq!(
110 parse_git_url_https("https://gitlab.com/hackeraudit/web.git"),
111 Some(GitUrlComponents {
112 domain: "gitlab.com".to_string(),
113 username: "hackeraudit".to_string(),
114 repo: "web.git".to_string(),
115 suffix: String::new()
116 })
117 );
118 assert_eq!(
119 parse_git_url_https("https://gitlab.com/hackeraudit/web.git/"),
120 Some(GitUrlComponents {
121 domain: "gitlab.com".to_string(),
122 username: "hackeraudit".to_string(),
123 repo: "web.git".to_string(),
124 suffix: String::new()
125 })
126 );
127 assert_eq!(
128 parse_git_url_https("https://gitlab.com/hackeraudit/web.git/////////"),
129 Some(GitUrlComponents {
130 domain: "gitlab.com".to_string(),
131 username: "hackeraudit".to_string(),
132 repo: "web.git".to_string(),
133 suffix: String::new()
134 })
135 );
136}
137
138#[must_use]
139pub fn https_to_git_url(http_url: &str) -> Option<String> {
140 parse_git_url_https(http_url).map(|components| {
141 format!(
142 "git@{}:{}/{}{}",
143 components.domain, components.username, components.repo, components.suffix
144 )
145 })
146}
147
148#[test]
149fn https_to_git_url_test() {
150 assert_eq!(
151 https_to_git_url("https://github.com/dpc/trust"),
152 Some("git@github.com:dpc/trust.git".into())
153 );
154 assert_eq!(
155 https_to_git_url("https://gitlab.com/hackeraudit/web.git"),
156 Some("git@gitlab.com:hackeraudit/web.git".into())
157 );
158 assert_eq!(
159 https_to_git_url("https://gitlab.com/hackeraudit/web.git/"),
160 Some("git@gitlab.com:hackeraudit/web.git".into())
161 );
162 assert_eq!(
163 https_to_git_url("https://gitlab.com/hackeraudit/web.git/////////"),
164 Some("git@gitlab.com:hackeraudit/web.git".into())
165 );
166 assert_eq!(
167 https_to_git_url("https://git.sr.ht/~ireas/crev-proofs"),
168 Some("git@git.sr.ht:~ireas/crev-proofs".into())
169 );
170 assert_eq!(https_to_git_url("https://example.com/foo/bar"), None);
171}