1use std::str::FromStr;
2
3use url::Url;
4
5use git::{
6 BuildCommitPermalinkParams, BuildPermalinkParams, GitHostingProvider, ParsedGitRemote,
7 RemoteUrl,
8};
9
10pub struct Sourcehut;
11
12impl GitHostingProvider for Sourcehut {
13 fn name(&self) -> String {
14 "Gitee".to_string()
15 }
16
17 fn base_url(&self) -> Url {
18 Url::parse("https://git.sr.ht").unwrap()
19 }
20
21 fn supports_avatars(&self) -> bool {
22 false
23 }
24
25 fn format_line_number(&self, line: u32) -> String {
26 format!("L{line}")
27 }
28
29 fn format_line_numbers(&self, start_line: u32, end_line: u32) -> String {
30 format!("L{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 != "git.sr.ht" {
38 return None;
39 }
40
41 let mut path_segments = url.path_segments()?;
42 let owner = path_segments.next()?;
43 // We don't trim the `.git` suffix here like we do elsewhere, as
44 // sourcehut treats a repo with `.git` suffix as a separate repo.
45 //
46 // For example, `git@git.sr.ht:~username/repo` and `git@git.sr.ht:~username/repo.git`
47 // are two distinct repositories.
48 let repo = path_segments.next()?;
49
50 Some(ParsedGitRemote {
51 owner: owner.into(),
52 repo: repo.into(),
53 })
54 }
55
56 fn build_commit_permalink(
57 &self,
58 remote: &ParsedGitRemote,
59 params: BuildCommitPermalinkParams,
60 ) -> Url {
61 let BuildCommitPermalinkParams { sha } = params;
62 let ParsedGitRemote { owner, repo } = remote;
63
64 self.base_url()
65 .join(&format!("~{owner}/{repo}/commit/{sha}"))
66 .unwrap()
67 }
68
69 fn build_permalink(&self, remote: ParsedGitRemote, params: BuildPermalinkParams) -> Url {
70 let ParsedGitRemote { owner, repo } = remote;
71 let BuildPermalinkParams {
72 sha,
73 path,
74 selection,
75 } = params;
76
77 let mut permalink = self
78 .base_url()
79 .join(&format!("~{owner}/{repo}/tree/{sha}/item/{path}"))
80 .unwrap();
81 permalink.set_fragment(
82 selection
83 .map(|selection| self.line_fragment(&selection))
84 .as_deref(),
85 );
86 permalink
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_build_sourcehut_permalink_from_ssh_url() {
96 let remote = ParsedGitRemote {
97 owner: "rajveermalviya".into(),
98 repo: "zed".into(),
99 };
100 let permalink = Sourcehut.build_permalink(
101 remote,
102 BuildPermalinkParams {
103 sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
104 path: "crates/editor/src/git/permalink.rs",
105 selection: None,
106 },
107 );
108
109 let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs";
110 assert_eq!(permalink.to_string(), expected_url.to_string())
111 }
112
113 #[test]
114 fn test_build_sourcehut_permalink_from_ssh_url_with_git_prefix() {
115 let remote = ParsedGitRemote {
116 owner: "rajveermalviya".into(),
117 repo: "zed.git".into(),
118 };
119 let permalink = Sourcehut.build_permalink(
120 remote,
121 BuildPermalinkParams {
122 sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
123 path: "crates/editor/src/git/permalink.rs",
124 selection: None,
125 },
126 );
127
128 let expected_url = "https://git.sr.ht/~rajveermalviya/zed.git/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs";
129 assert_eq!(permalink.to_string(), expected_url.to_string())
130 }
131
132 #[test]
133 fn test_build_sourcehut_permalink_from_ssh_url_single_line_selection() {
134 let remote = ParsedGitRemote {
135 owner: "rajveermalviya".into(),
136 repo: "zed".into(),
137 };
138 let permalink = Sourcehut.build_permalink(
139 remote,
140 BuildPermalinkParams {
141 sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
142 path: "crates/editor/src/git/permalink.rs",
143 selection: Some(6..6),
144 },
145 );
146
147 let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs#L7";
148 assert_eq!(permalink.to_string(), expected_url.to_string())
149 }
150
151 #[test]
152 fn test_build_sourcehut_permalink_from_ssh_url_multi_line_selection() {
153 let remote = ParsedGitRemote {
154 owner: "rajveermalviya".into(),
155 repo: "zed".into(),
156 };
157 let permalink = Sourcehut.build_permalink(
158 remote,
159 BuildPermalinkParams {
160 sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
161 path: "crates/editor/src/git/permalink.rs",
162 selection: Some(23..47),
163 },
164 );
165
166 let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs#L24-48";
167 assert_eq!(permalink.to_string(), expected_url.to_string())
168 }
169
170 #[test]
171 fn test_build_sourcehut_permalink_from_https_url() {
172 let remote = ParsedGitRemote {
173 owner: "rajveermalviya".into(),
174 repo: "zed".into(),
175 };
176 let permalink = Sourcehut.build_permalink(
177 remote,
178 BuildPermalinkParams {
179 sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
180 path: "crates/zed/src/main.rs",
181 selection: None,
182 },
183 );
184
185 let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/zed/src/main.rs";
186 assert_eq!(permalink.to_string(), expected_url.to_string())
187 }
188
189 #[test]
190 fn test_build_sourcehut_permalink_from_https_url_single_line_selection() {
191 let remote = ParsedGitRemote {
192 owner: "rajveermalviya".into(),
193 repo: "zed".into(),
194 };
195 let permalink = Sourcehut.build_permalink(
196 remote,
197 BuildPermalinkParams {
198 sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
199 path: "crates/zed/src/main.rs",
200 selection: Some(6..6),
201 },
202 );
203
204 let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/zed/src/main.rs#L7";
205 assert_eq!(permalink.to_string(), expected_url.to_string())
206 }
207
208 #[test]
209 fn test_build_sourcehut_permalink_from_https_url_multi_line_selection() {
210 let remote = ParsedGitRemote {
211 owner: "rajveermalviya".into(),
212 repo: "zed".into(),
213 };
214 let permalink = Sourcehut.build_permalink(
215 remote,
216 BuildPermalinkParams {
217 sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
218 path: "crates/zed/src/main.rs",
219 selection: Some(23..47),
220 },
221 );
222
223 let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/zed/src/main.rs#L24-48";
224 assert_eq!(permalink.to_string(), expected_url.to_string())
225 }
226}