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