sourcehut.rs

  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}