bitbucket.rs

  1use std::str::FromStr;
  2
  3use url::Url;
  4
  5use git::{
  6    BuildCommitPermalinkParams, BuildPermalinkParams, GitHostingProvider, ParsedGitRemote,
  7    RemoteUrl,
  8};
  9
 10pub struct Bitbucket;
 11
 12impl GitHostingProvider for Bitbucket {
 13    fn name(&self) -> String {
 14        "Bitbucket".to_string()
 15    }
 16
 17    fn base_url(&self) -> Url {
 18        Url::parse("https://bitbucket.org").unwrap()
 19    }
 20
 21    fn supports_avatars(&self) -> bool {
 22        false
 23    }
 24
 25    fn format_line_number(&self, line: u32) -> String {
 26        format!("lines-{line}")
 27    }
 28
 29    fn format_line_numbers(&self, start_line: u32, end_line: u32) -> String {
 30        format!("lines-{start_line}:{end_line}")
 31    }
 32
 33    fn parse_remote_url(&self, url: &str) -> Option<ParsedGitRemote> {
 34        let url = RemoteUrl::from_str(url).ok()?;
 35
 36        let host = url.host_str()?;
 37        if host != "bitbucket.org" {
 38            return None;
 39        }
 40
 41        let mut path_segments = url.path_segments()?;
 42        let owner = path_segments.next()?;
 43        let repo = path_segments.next()?.trim_end_matches(".git");
 44
 45        Some(ParsedGitRemote {
 46            owner: owner.into(),
 47            repo: repo.into(),
 48        })
 49    }
 50
 51    fn build_commit_permalink(
 52        &self,
 53        remote: &ParsedGitRemote,
 54        params: BuildCommitPermalinkParams,
 55    ) -> Url {
 56        let BuildCommitPermalinkParams { sha } = params;
 57        let ParsedGitRemote { owner, repo } = remote;
 58
 59        self.base_url()
 60            .join(&format!("{owner}/{repo}/commits/{sha}"))
 61            .unwrap()
 62    }
 63
 64    fn build_permalink(&self, remote: ParsedGitRemote, params: BuildPermalinkParams) -> Url {
 65        let ParsedGitRemote { owner, repo } = remote;
 66        let BuildPermalinkParams {
 67            sha,
 68            path,
 69            selection,
 70        } = params;
 71
 72        let mut permalink = self
 73            .base_url()
 74            .join(&format!("{owner}/{repo}/src/{sha}/{path}"))
 75            .unwrap();
 76        permalink.set_fragment(
 77            selection
 78                .map(|selection| self.line_fragment(&selection))
 79                .as_deref(),
 80        );
 81        permalink
 82    }
 83}
 84
 85#[cfg(test)]
 86mod tests {
 87    use pretty_assertions::assert_eq;
 88
 89    use super::*;
 90
 91    #[test]
 92    fn test_parse_remote_url_given_ssh_url() {
 93        let parsed_remote = Bitbucket
 94            .parse_remote_url("git@bitbucket.org:zed-industries/zed.git")
 95            .unwrap();
 96
 97        assert_eq!(
 98            parsed_remote,
 99            ParsedGitRemote {
100                owner: "zed-industries".into(),
101                repo: "zed".into(),
102            }
103        );
104    }
105
106    #[test]
107    fn test_parse_remote_url_given_https_url() {
108        let parsed_remote = Bitbucket
109            .parse_remote_url("https://bitbucket.org/zed-industries/zed.git")
110            .unwrap();
111
112        assert_eq!(
113            parsed_remote,
114            ParsedGitRemote {
115                owner: "zed-industries".into(),
116                repo: "zed".into(),
117            }
118        );
119    }
120
121    #[test]
122    fn test_parse_remote_url_given_https_url_with_username() {
123        let parsed_remote = Bitbucket
124            .parse_remote_url("https://thorstenballzed@bitbucket.org/zed-industries/zed.git")
125            .unwrap();
126
127        assert_eq!(
128            parsed_remote,
129            ParsedGitRemote {
130                owner: "zed-industries".into(),
131                repo: "zed".into(),
132            }
133        );
134    }
135
136    #[test]
137    fn test_build_bitbucket_permalink() {
138        let permalink = Bitbucket.build_permalink(
139            ParsedGitRemote {
140                owner: "zed-industries".into(),
141                repo: "zed".into(),
142            },
143            BuildPermalinkParams {
144                sha: "f00b4r",
145                path: "main.rs",
146                selection: None,
147            },
148        );
149
150        let expected_url = "https://bitbucket.org/zed-industries/zed/src/f00b4r/main.rs";
151        assert_eq!(permalink.to_string(), expected_url.to_string())
152    }
153
154    #[test]
155    fn test_build_bitbucket_permalink_with_single_line_selection() {
156        let permalink = Bitbucket.build_permalink(
157            ParsedGitRemote {
158                owner: "zed-industries".into(),
159                repo: "zed".into(),
160            },
161            BuildPermalinkParams {
162                sha: "f00b4r",
163                path: "main.rs",
164                selection: Some(6..6),
165            },
166        );
167
168        let expected_url = "https://bitbucket.org/zed-industries/zed/src/f00b4r/main.rs#lines-7";
169        assert_eq!(permalink.to_string(), expected_url.to_string())
170    }
171
172    #[test]
173    fn test_build_bitbucket_permalink_with_multi_line_selection() {
174        let permalink = Bitbucket.build_permalink(
175            ParsedGitRemote {
176                owner: "zed-industries".into(),
177                repo: "zed".into(),
178            },
179            BuildPermalinkParams {
180                sha: "f00b4r",
181                path: "main.rs",
182                selection: Some(23..47),
183            },
184        );
185
186        let expected_url =
187            "https://bitbucket.org/zed-industries/zed/src/f00b4r/main.rs#lines-24:48";
188        assert_eq!(permalink.to_string(), expected_url.to_string())
189    }
190}