git_whennes/
forge.rs

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}