permalink.rs

  1use std::ops::Range;
  2
  3use anyhow::{anyhow, Result};
  4use url::Url;
  5
  6use crate::hosting_provider::HostingProvider;
  7
  8pub struct BuildPermalinkParams<'a> {
  9    pub remote_url: &'a str,
 10    pub sha: &'a str,
 11    pub path: &'a str,
 12    pub selection: Option<Range<u32>>,
 13}
 14
 15pub fn build_permalink(params: BuildPermalinkParams) -> Result<Url> {
 16    let BuildPermalinkParams {
 17        remote_url,
 18        sha,
 19        path,
 20        selection,
 21    } = params;
 22
 23    let ParsedGitRemote {
 24        provider,
 25        owner,
 26        repo,
 27    } = parse_git_remote_url(remote_url)
 28        .ok_or_else(|| anyhow!("failed to parse Git remote URL"))?;
 29
 30    let path = match provider {
 31        HostingProvider::Github => format!("{owner}/{repo}/blob/{sha}/{path}"),
 32        HostingProvider::Gitlab => format!("{owner}/{repo}/-/blob/{sha}/{path}"),
 33        HostingProvider::Gitee => format!("{owner}/{repo}/blob/{sha}/{path}"),
 34        HostingProvider::Bitbucket => format!("{owner}/{repo}/src/{sha}/{path}"),
 35        HostingProvider::Sourcehut => format!("~{owner}/{repo}/tree/{sha}/item/{path}"),
 36        HostingProvider::Codeberg => format!("{owner}/{repo}/src/commit/{sha}/{path}"),
 37    };
 38    let line_fragment = selection.map(|selection| provider.line_fragment(&selection));
 39
 40    let mut permalink = provider.base_url().join(&path).unwrap();
 41    permalink.set_fragment(line_fragment.as_deref());
 42    Ok(permalink)
 43}
 44
 45#[derive(Debug)]
 46pub struct ParsedGitRemote<'a> {
 47    pub provider: HostingProvider,
 48    pub owner: &'a str,
 49    pub repo: &'a str,
 50}
 51
 52pub struct BuildCommitPermalinkParams<'a> {
 53    pub remote: &'a ParsedGitRemote<'a>,
 54    pub sha: &'a str,
 55}
 56
 57pub fn build_commit_permalink(params: BuildCommitPermalinkParams) -> Url {
 58    let BuildCommitPermalinkParams { sha, remote } = params;
 59
 60    let ParsedGitRemote {
 61        provider,
 62        owner,
 63        repo,
 64    } = remote;
 65
 66    let path = match provider {
 67        HostingProvider::Github => format!("{owner}/{repo}/commit/{sha}"),
 68        HostingProvider::Gitlab => format!("{owner}/{repo}/-/commit/{sha}"),
 69        HostingProvider::Gitee => format!("{owner}/{repo}/commit/{sha}"),
 70        HostingProvider::Bitbucket => format!("{owner}/{repo}/commits/{sha}"),
 71        HostingProvider::Sourcehut => format!("~{owner}/{repo}/commit/{sha}"),
 72        HostingProvider::Codeberg => format!("{owner}/{repo}/commit/{sha}"),
 73    };
 74
 75    provider.base_url().join(&path).unwrap()
 76}
 77
 78pub fn parse_git_remote_url(url: &str) -> Option<ParsedGitRemote> {
 79    if url.starts_with("git@github.com:") || url.starts_with("https://github.com/") {
 80        let repo_with_owner = url
 81            .trim_start_matches("git@github.com:")
 82            .trim_start_matches("https://github.com/")
 83            .trim_end_matches(".git");
 84
 85        let (owner, repo) = repo_with_owner.split_once('/')?;
 86
 87        return Some(ParsedGitRemote {
 88            provider: HostingProvider::Github,
 89            owner,
 90            repo,
 91        });
 92    }
 93
 94    if url.starts_with("git@gitlab.com:") || url.starts_with("https://gitlab.com/") {
 95        let repo_with_owner = url
 96            .trim_start_matches("git@gitlab.com:")
 97            .trim_start_matches("https://gitlab.com/")
 98            .trim_end_matches(".git");
 99
100        let (owner, repo) = repo_with_owner.split_once('/')?;
101
102        return Some(ParsedGitRemote {
103            provider: HostingProvider::Gitlab,
104            owner,
105            repo,
106        });
107    }
108
109    if url.starts_with("git@gitee.com:") || url.starts_with("https://gitee.com/") {
110        let repo_with_owner = url
111            .trim_start_matches("git@gitee.com:")
112            .trim_start_matches("https://gitee.com/")
113            .trim_end_matches(".git");
114
115        let (owner, repo) = repo_with_owner.split_once('/')?;
116
117        return Some(ParsedGitRemote {
118            provider: HostingProvider::Gitee,
119            owner,
120            repo,
121        });
122    }
123
124    if url.contains("bitbucket.org") {
125        let (_, repo_with_owner) = url.trim_end_matches(".git").split_once("bitbucket.org")?;
126        let (owner, repo) = repo_with_owner
127            .trim_start_matches('/')
128            .trim_start_matches(':')
129            .split_once('/')?;
130
131        return Some(ParsedGitRemote {
132            provider: HostingProvider::Bitbucket,
133            owner,
134            repo,
135        });
136    }
137
138    if url.starts_with("git@git.sr.ht:") || url.starts_with("https://git.sr.ht/") {
139        // sourcehut indicates a repo with '.git' suffix as a separate repo.
140        // For example, "git@git.sr.ht:~username/repo" and "git@git.sr.ht:~username/repo.git"
141        // are two distinct repositories.
142        let repo_with_owner = url
143            .trim_start_matches("git@git.sr.ht:~")
144            .trim_start_matches("https://git.sr.ht/~");
145
146        let (owner, repo) = repo_with_owner.split_once('/')?;
147
148        return Some(ParsedGitRemote {
149            provider: HostingProvider::Sourcehut,
150            owner,
151            repo,
152        });
153    }
154
155    if url.starts_with("git@codeberg.org:") || url.starts_with("https://codeberg.org/") {
156        let repo_with_owner = url
157            .trim_start_matches("git@codeberg.org:")
158            .trim_start_matches("https://codeberg.org/")
159            .trim_end_matches(".git");
160
161        let (owner, repo) = repo_with_owner.split_once('/')?;
162
163        return Some(ParsedGitRemote {
164            provider: HostingProvider::Codeberg,
165            owner,
166            repo,
167        });
168    }
169
170    None
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176
177    #[test]
178    fn test_build_github_permalink_from_ssh_url() {
179        let permalink = build_permalink(BuildPermalinkParams {
180            remote_url: "git@github.com:zed-industries/zed.git",
181            sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
182            path: "crates/editor/src/git/permalink.rs",
183            selection: None,
184        })
185        .unwrap();
186
187        let expected_url = "https://github.com/zed-industries/zed/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs";
188        assert_eq!(permalink.to_string(), expected_url.to_string())
189    }
190
191    #[test]
192    fn test_build_github_permalink_from_ssh_url_single_line_selection() {
193        let permalink = build_permalink(BuildPermalinkParams {
194            remote_url: "git@github.com:zed-industries/zed.git",
195            sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
196            path: "crates/editor/src/git/permalink.rs",
197            selection: Some(6..6),
198        })
199        .unwrap();
200
201        let expected_url = "https://github.com/zed-industries/zed/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs#L7";
202        assert_eq!(permalink.to_string(), expected_url.to_string())
203    }
204
205    #[test]
206    fn test_build_github_permalink_from_ssh_url_multi_line_selection() {
207        let permalink = build_permalink(BuildPermalinkParams {
208            remote_url: "git@github.com:zed-industries/zed.git",
209            sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
210            path: "crates/editor/src/git/permalink.rs",
211            selection: Some(23..47),
212        })
213        .unwrap();
214
215        let expected_url = "https://github.com/zed-industries/zed/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs#L24-L48";
216        assert_eq!(permalink.to_string(), expected_url.to_string())
217    }
218
219    #[test]
220    fn test_build_github_permalink_from_https_url() {
221        let permalink = build_permalink(BuildPermalinkParams {
222            remote_url: "https://github.com/zed-industries/zed.git",
223            sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
224            path: "crates/zed/src/main.rs",
225            selection: None,
226        })
227        .unwrap();
228
229        let expected_url = "https://github.com/zed-industries/zed/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs";
230        assert_eq!(permalink.to_string(), expected_url.to_string())
231    }
232
233    #[test]
234    fn test_build_github_permalink_from_https_url_single_line_selection() {
235        let permalink = build_permalink(BuildPermalinkParams {
236            remote_url: "https://github.com/zed-industries/zed.git",
237            sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
238            path: "crates/zed/src/main.rs",
239            selection: Some(6..6),
240        })
241        .unwrap();
242
243        let expected_url = "https://github.com/zed-industries/zed/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs#L7";
244        assert_eq!(permalink.to_string(), expected_url.to_string())
245    }
246
247    #[test]
248    fn test_build_github_permalink_from_https_url_multi_line_selection() {
249        let permalink = build_permalink(BuildPermalinkParams {
250            remote_url: "https://github.com/zed-industries/zed.git",
251            sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
252            path: "crates/zed/src/main.rs",
253            selection: Some(23..47),
254        })
255        .unwrap();
256
257        let expected_url = "https://github.com/zed-industries/zed/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs#L24-L48";
258        assert_eq!(permalink.to_string(), expected_url.to_string())
259    }
260
261    #[test]
262    fn test_build_gitlab_permalink_from_ssh_url() {
263        let permalink = build_permalink(BuildPermalinkParams {
264            remote_url: "git@gitlab.com:zed-industries/zed.git",
265            sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
266            path: "crates/editor/src/git/permalink.rs",
267            selection: None,
268        })
269        .unwrap();
270
271        let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs";
272        assert_eq!(permalink.to_string(), expected_url.to_string())
273    }
274
275    #[test]
276    fn test_build_gitlab_permalink_from_ssh_url_single_line_selection() {
277        let permalink = build_permalink(BuildPermalinkParams {
278            remote_url: "git@gitlab.com:zed-industries/zed.git",
279            sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
280            path: "crates/editor/src/git/permalink.rs",
281            selection: Some(6..6),
282        })
283        .unwrap();
284
285        let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs#L7";
286        assert_eq!(permalink.to_string(), expected_url.to_string())
287    }
288
289    #[test]
290    fn test_build_gitlab_permalink_from_ssh_url_multi_line_selection() {
291        let permalink = build_permalink(BuildPermalinkParams {
292            remote_url: "git@gitlab.com:zed-industries/zed.git",
293            sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
294            path: "crates/editor/src/git/permalink.rs",
295            selection: Some(23..47),
296        })
297        .unwrap();
298
299        let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs#L24-48";
300        assert_eq!(permalink.to_string(), expected_url.to_string())
301    }
302
303    #[test]
304    fn test_build_gitlab_permalink_from_https_url() {
305        let permalink = build_permalink(BuildPermalinkParams {
306            remote_url: "https://gitlab.com/zed-industries/zed.git",
307            sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
308            path: "crates/zed/src/main.rs",
309            selection: None,
310        })
311        .unwrap();
312
313        let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs";
314        assert_eq!(permalink.to_string(), expected_url.to_string())
315    }
316
317    #[test]
318    fn test_build_gitlab_permalink_from_https_url_single_line_selection() {
319        let permalink = build_permalink(BuildPermalinkParams {
320            remote_url: "https://gitlab.com/zed-industries/zed.git",
321            sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
322            path: "crates/zed/src/main.rs",
323            selection: Some(6..6),
324        })
325        .unwrap();
326
327        let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs#L7";
328        assert_eq!(permalink.to_string(), expected_url.to_string())
329    }
330
331    #[test]
332    fn test_build_gitlab_permalink_from_https_url_multi_line_selection() {
333        let permalink = build_permalink(BuildPermalinkParams {
334            remote_url: "https://gitlab.com/zed-industries/zed.git",
335            sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
336            path: "crates/zed/src/main.rs",
337            selection: Some(23..47),
338        })
339        .unwrap();
340
341        let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs#L24-48";
342        assert_eq!(permalink.to_string(), expected_url.to_string())
343    }
344
345    #[test]
346    fn test_build_gitee_permalink_from_ssh_url() {
347        let permalink = build_permalink(BuildPermalinkParams {
348            remote_url: "git@gitee.com:libkitten/zed.git",
349            sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
350            path: "crates/editor/src/git/permalink.rs",
351            selection: None,
352        })
353        .unwrap();
354
355        let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/editor/src/git/permalink.rs";
356        assert_eq!(permalink.to_string(), expected_url.to_string())
357    }
358
359    #[test]
360    fn test_build_gitee_permalink_from_ssh_url_single_line_selection() {
361        let permalink = build_permalink(BuildPermalinkParams {
362            remote_url: "git@gitee.com:libkitten/zed.git",
363            sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
364            path: "crates/editor/src/git/permalink.rs",
365            selection: Some(6..6),
366        })
367        .unwrap();
368
369        let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/editor/src/git/permalink.rs#L7";
370        assert_eq!(permalink.to_string(), expected_url.to_string())
371    }
372
373    #[test]
374    fn test_build_gitee_permalink_from_ssh_url_multi_line_selection() {
375        let permalink = build_permalink(BuildPermalinkParams {
376            remote_url: "git@gitee.com:libkitten/zed.git",
377            sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
378            path: "crates/editor/src/git/permalink.rs",
379            selection: Some(23..47),
380        })
381        .unwrap();
382
383        let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/editor/src/git/permalink.rs#L24-48";
384        assert_eq!(permalink.to_string(), expected_url.to_string())
385    }
386
387    #[test]
388    fn test_build_gitee_permalink_from_https_url() {
389        let permalink = build_permalink(BuildPermalinkParams {
390            remote_url: "https://gitee.com/libkitten/zed.git",
391            sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
392            path: "crates/zed/src/main.rs",
393            selection: None,
394        })
395        .unwrap();
396
397        let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/zed/src/main.rs";
398        assert_eq!(permalink.to_string(), expected_url.to_string())
399    }
400
401    #[test]
402    fn test_build_gitee_permalink_from_https_url_single_line_selection() {
403        let permalink = build_permalink(BuildPermalinkParams {
404            remote_url: "https://gitee.com/libkitten/zed.git",
405            sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
406            path: "crates/zed/src/main.rs",
407            selection: Some(6..6),
408        })
409        .unwrap();
410
411        let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/zed/src/main.rs#L7";
412        assert_eq!(permalink.to_string(), expected_url.to_string())
413    }
414
415    #[test]
416    fn test_build_gitee_permalink_from_https_url_multi_line_selection() {
417        let permalink = build_permalink(BuildPermalinkParams {
418            remote_url: "https://gitee.com/libkitten/zed.git",
419            sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
420            path: "crates/zed/src/main.rs",
421            selection: Some(23..47),
422        })
423        .unwrap();
424        let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/zed/src/main.rs#L24-48";
425        assert_eq!(permalink.to_string(), expected_url.to_string())
426    }
427
428    #[test]
429    fn test_parse_git_remote_url_bitbucket_https_with_username() {
430        let url = "https://thorstenballzed@bitbucket.org/thorstenzed/testingrepo.git";
431        let parsed = parse_git_remote_url(url).unwrap();
432        assert!(matches!(parsed.provider, HostingProvider::Bitbucket));
433        assert_eq!(parsed.owner, "thorstenzed");
434        assert_eq!(parsed.repo, "testingrepo");
435    }
436
437    #[test]
438    fn test_parse_git_remote_url_bitbucket_https_without_username() {
439        let url = "https://bitbucket.org/thorstenzed/testingrepo.git";
440        let parsed = parse_git_remote_url(url).unwrap();
441        assert!(matches!(parsed.provider, HostingProvider::Bitbucket));
442        assert_eq!(parsed.owner, "thorstenzed");
443        assert_eq!(parsed.repo, "testingrepo");
444    }
445
446    #[test]
447    fn test_parse_git_remote_url_bitbucket_git() {
448        let url = "git@bitbucket.org:thorstenzed/testingrepo.git";
449        let parsed = parse_git_remote_url(url).unwrap();
450        assert!(matches!(parsed.provider, HostingProvider::Bitbucket));
451        assert_eq!(parsed.owner, "thorstenzed");
452        assert_eq!(parsed.repo, "testingrepo");
453    }
454
455    #[test]
456    fn test_build_bitbucket_permalink_from_ssh_url() {
457        let permalink = build_permalink(BuildPermalinkParams {
458            remote_url: "git@bitbucket.org:thorstenzed/testingrepo.git",
459            sha: "f00b4r",
460            path: "main.rs",
461            selection: None,
462        })
463        .unwrap();
464
465        let expected_url = "https://bitbucket.org/thorstenzed/testingrepo/src/f00b4r/main.rs";
466        assert_eq!(permalink.to_string(), expected_url.to_string())
467    }
468
469    #[test]
470    fn test_build_bitbucket_permalink_from_ssh_url_single_line_selection() {
471        let permalink = build_permalink(BuildPermalinkParams {
472            remote_url: "git@bitbucket.org:thorstenzed/testingrepo.git",
473            sha: "f00b4r",
474            path: "main.rs",
475            selection: Some(6..6),
476        })
477        .unwrap();
478
479        let expected_url =
480            "https://bitbucket.org/thorstenzed/testingrepo/src/f00b4r/main.rs#lines-7";
481        assert_eq!(permalink.to_string(), expected_url.to_string())
482    }
483
484    #[test]
485    fn test_build_bitbucket_permalink_from_ssh_url_multi_line_selection() {
486        let permalink = build_permalink(BuildPermalinkParams {
487            remote_url: "git@bitbucket.org:thorstenzed/testingrepo.git",
488            sha: "f00b4r",
489            path: "main.rs",
490            selection: Some(23..47),
491        })
492        .unwrap();
493
494        let expected_url =
495            "https://bitbucket.org/thorstenzed/testingrepo/src/f00b4r/main.rs#lines-24:48";
496        assert_eq!(permalink.to_string(), expected_url.to_string())
497    }
498
499    #[test]
500    fn test_build_sourcehut_permalink_from_ssh_url() {
501        let permalink = build_permalink(BuildPermalinkParams {
502            remote_url: "git@git.sr.ht:~rajveermalviya/zed",
503            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
504            path: "crates/editor/src/git/permalink.rs",
505            selection: None,
506        })
507        .unwrap();
508
509        let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs";
510        assert_eq!(permalink.to_string(), expected_url.to_string())
511    }
512
513    #[test]
514    fn test_build_sourcehut_permalink_from_ssh_url_with_git_prefix() {
515        let permalink = build_permalink(BuildPermalinkParams {
516            remote_url: "git@git.sr.ht:~rajveermalviya/zed.git",
517            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
518            path: "crates/editor/src/git/permalink.rs",
519            selection: None,
520        })
521        .unwrap();
522
523        let expected_url = "https://git.sr.ht/~rajveermalviya/zed.git/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs";
524        assert_eq!(permalink.to_string(), expected_url.to_string())
525    }
526
527    #[test]
528    fn test_build_sourcehut_permalink_from_ssh_url_single_line_selection() {
529        let permalink = build_permalink(BuildPermalinkParams {
530            remote_url: "git@git.sr.ht:~rajveermalviya/zed",
531            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
532            path: "crates/editor/src/git/permalink.rs",
533            selection: Some(6..6),
534        })
535        .unwrap();
536
537        let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs#L7";
538        assert_eq!(permalink.to_string(), expected_url.to_string())
539    }
540
541    #[test]
542    fn test_build_sourcehut_permalink_from_ssh_url_multi_line_selection() {
543        let permalink = build_permalink(BuildPermalinkParams {
544            remote_url: "git@git.sr.ht:~rajveermalviya/zed",
545            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
546            path: "crates/editor/src/git/permalink.rs",
547            selection: Some(23..47),
548        })
549        .unwrap();
550
551        let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs#L24-48";
552        assert_eq!(permalink.to_string(), expected_url.to_string())
553    }
554
555    #[test]
556    fn test_build_sourcehut_permalink_from_https_url() {
557        let permalink = build_permalink(BuildPermalinkParams {
558            remote_url: "https://git.sr.ht/~rajveermalviya/zed",
559            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
560            path: "crates/zed/src/main.rs",
561            selection: None,
562        })
563        .unwrap();
564
565        let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/zed/src/main.rs";
566        assert_eq!(permalink.to_string(), expected_url.to_string())
567    }
568
569    #[test]
570    fn test_build_sourcehut_permalink_from_https_url_single_line_selection() {
571        let permalink = build_permalink(BuildPermalinkParams {
572            remote_url: "https://git.sr.ht/~rajveermalviya/zed",
573            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
574            path: "crates/zed/src/main.rs",
575            selection: Some(6..6),
576        })
577        .unwrap();
578
579        let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/zed/src/main.rs#L7";
580        assert_eq!(permalink.to_string(), expected_url.to_string())
581    }
582
583    #[test]
584    fn test_build_sourcehut_permalink_from_https_url_multi_line_selection() {
585        let permalink = build_permalink(BuildPermalinkParams {
586            remote_url: "https://git.sr.ht/~rajveermalviya/zed",
587            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
588            path: "crates/zed/src/main.rs",
589            selection: Some(23..47),
590        })
591        .unwrap();
592
593        let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/zed/src/main.rs#L24-48";
594        assert_eq!(permalink.to_string(), expected_url.to_string())
595    }
596
597    #[test]
598    fn test_build_codeberg_permalink_from_ssh_url() {
599        let permalink = build_permalink(BuildPermalinkParams {
600            remote_url: "git@codeberg.org:rajveermalviya/zed.git",
601            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
602            path: "crates/editor/src/git/permalink.rs",
603            selection: None,
604        })
605        .unwrap();
606
607        let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/editor/src/git/permalink.rs";
608        assert_eq!(permalink.to_string(), expected_url.to_string())
609    }
610
611    #[test]
612    fn test_build_codeberg_permalink_from_ssh_url_single_line_selection() {
613        let permalink = build_permalink(BuildPermalinkParams {
614            remote_url: "git@codeberg.org:rajveermalviya/zed.git",
615            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
616            path: "crates/editor/src/git/permalink.rs",
617            selection: Some(6..6),
618        })
619        .unwrap();
620
621        let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/editor/src/git/permalink.rs#L7";
622        assert_eq!(permalink.to_string(), expected_url.to_string())
623    }
624
625    #[test]
626    fn test_build_codeberg_permalink_from_ssh_url_multi_line_selection() {
627        let permalink = build_permalink(BuildPermalinkParams {
628            remote_url: "git@codeberg.org:rajveermalviya/zed.git",
629            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
630            path: "crates/editor/src/git/permalink.rs",
631            selection: Some(23..47),
632        })
633        .unwrap();
634
635        let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/editor/src/git/permalink.rs#L24-L48";
636        assert_eq!(permalink.to_string(), expected_url.to_string())
637    }
638
639    #[test]
640    fn test_build_codeberg_permalink_from_https_url() {
641        let permalink = build_permalink(BuildPermalinkParams {
642            remote_url: "https://codeberg.org/rajveermalviya/zed.git",
643            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
644            path: "crates/zed/src/main.rs",
645            selection: None,
646        })
647        .unwrap();
648
649        let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/zed/src/main.rs";
650        assert_eq!(permalink.to_string(), expected_url.to_string())
651    }
652
653    #[test]
654    fn test_build_codeberg_permalink_from_https_url_single_line_selection() {
655        let permalink = build_permalink(BuildPermalinkParams {
656            remote_url: "https://codeberg.org/rajveermalviya/zed.git",
657            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
658            path: "crates/zed/src/main.rs",
659            selection: Some(6..6),
660        })
661        .unwrap();
662
663        let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/zed/src/main.rs#L7";
664        assert_eq!(permalink.to_string(), expected_url.to_string())
665    }
666
667    #[test]
668    fn test_build_codeberg_permalink_from_https_url_multi_line_selection() {
669        let permalink = build_permalink(BuildPermalinkParams {
670            remote_url: "https://codeberg.org/rajveermalviya/zed.git",
671            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
672            path: "crates/zed/src/main.rs",
673            selection: Some(23..47),
674        })
675        .unwrap();
676
677        let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/zed/src/main.rs#L24-L48";
678        assert_eq!(permalink.to_string(), expected_url.to_string())
679    }
680}