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