1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use git2::{Commit, Repository};
use git_url_parse::GitUrl;
use crate::error::{Error, Result};
pub fn get_url(
repo: &Repository,
remote: &str,
commit: Commit,
) -> Result<String> {
let remote =
repo.find_remote(remote)
.map_err(|e| Error::FindRemoteError {
remote: remote.to_string(),
source: e,
})?;
let remote_url = remote.url().unwrap();
log::debug!("Found remote URL {} for 'origin'", remote_url);
let remote_url = GitUrl::parse(remote_url).unwrap();
let forge = Forge::from_url(&remote_url)?;
let url = forge.get_url(&remote_url, commit)?;
Ok(url)
}
trait Forger {
fn get_url(&self, url: &GitUrl, commit: Commit) -> Result<String>;
}
enum Forge {
#[cfg(feature = "sourcehut")]
SourceHut(sourcehut::SourceHut),
#[cfg(feature = "github")]
GitHub(github::GitHub),
}
impl Forger for Forge {
fn get_url(&self, url: &GitUrl, commit: Commit) -> Result<String> {
match self {
#[cfg(feature = "sourcehut")]
Forge::SourceHut(s) => s.get_url(url, commit),
#[cfg(feature = "github")]
Forge::GitHub(g) => g.get_url(url, commit),
}
}
}
impl Forge {
fn from_url(url: &GitUrl) -> Result<Self> {
match &url.host {
Some(u) => match &u[..] {
#[cfg(feature = "sourcehut")]
"git.sr.ht" => Ok(Forge::SourceHut(sourcehut::SourceHut {})),
#[cfg(feature = "github")]
"github.com" => Ok(Forge::GitHub(github::GitHub {})),
v => unimplemented!("Host {} not handled", v),
},
None => unimplemented!("URL {} has no domain", url),
}
}
}
#[cfg(feature = "sourcehut")]
mod sourcehut {
use git2::Commit;
use git_url_parse::GitUrl;
use super::Forger;
use crate::error::Result;
pub struct SourceHut {}
impl Forger for SourceHut {
fn get_url(&self, url: &GitUrl, commit: Commit) -> Result<String> {
Ok(format!(
"https://git.sr.ht/{}/commit/{}",
url.fullname,
commit.id()
))
}
}
}
#[cfg(feature = "github")]
mod github {
use git2::Commit;
use git_url_parse::GitUrl;
use super::Forger;
use crate::error::Result;
pub struct GitHub {}
impl Forger for GitHub {
fn get_url(&self, url: &GitUrl, commit: Commit) -> Result<String> {
let repo = url.fullname.clone();
let msg = commit.message().unwrap_or_default().to_string();
let re = regex::Regex::new(r#"^Merge pull request #(\d+) from"#)
.unwrap();
for line in msg.lines() {
if let Some(captures) = re.captures(line) {
let pr_num = &captures[1];
return Ok(format!(
"https://github.com/{}/pull/{}",
repo, pr_num
));
}
}
Ok(format!(
"https://github.com/{}/commit/{}",
repo,
commit.id()
))
}
}
}