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        "SourceHut".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()?.trim_start_matches('~');
 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 pretty_assertions::assert_eq;
 93
 94    use super::*;
 95
 96    #[test]
 97    fn test_parse_remote_url_given_ssh_url() {
 98        let parsed_remote = Sourcehut
 99            .parse_remote_url("git@git.sr.ht:~zed-industries/zed")
100            .unwrap();
101
102        assert_eq!(
103            parsed_remote,
104            ParsedGitRemote {
105                owner: "zed-industries".into(),
106                repo: "zed".into(),
107            }
108        );
109    }
110
111    #[test]
112    fn test_parse_remote_url_given_ssh_url_with_git_suffix() {
113        let parsed_remote = Sourcehut
114            .parse_remote_url("git@git.sr.ht:~zed-industries/zed.git")
115            .unwrap();
116
117        assert_eq!(
118            parsed_remote,
119            ParsedGitRemote {
120                owner: "zed-industries".into(),
121                repo: "zed.git".into(),
122            }
123        );
124    }
125
126    #[test]
127    fn test_parse_remote_url_given_https_url() {
128        let parsed_remote = Sourcehut
129            .parse_remote_url("https://git.sr.ht/~zed-industries/zed")
130            .unwrap();
131
132        assert_eq!(
133            parsed_remote,
134            ParsedGitRemote {
135                owner: "zed-industries".into(),
136                repo: "zed".into(),
137            }
138        );
139    }
140
141    #[test]
142    fn test_build_sourcehut_permalink() {
143        let permalink = Sourcehut.build_permalink(
144            ParsedGitRemote {
145                owner: "zed-industries".into(),
146                repo: "zed".into(),
147            },
148            BuildPermalinkParams {
149                sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
150                path: "crates/editor/src/git/permalink.rs",
151                selection: None,
152            },
153        );
154
155        let expected_url = "https://git.sr.ht/~zed-industries/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs";
156        assert_eq!(permalink.to_string(), expected_url.to_string())
157    }
158
159    #[test]
160    fn test_build_sourcehut_permalink_with_git_suffix() {
161        let permalink = Sourcehut.build_permalink(
162            ParsedGitRemote {
163                owner: "zed-industries".into(),
164                repo: "zed.git".into(),
165            },
166            BuildPermalinkParams {
167                sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
168                path: "crates/editor/src/git/permalink.rs",
169                selection: None,
170            },
171        );
172
173        let expected_url = "https://git.sr.ht/~zed-industries/zed.git/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs";
174        assert_eq!(permalink.to_string(), expected_url.to_string())
175    }
176
177    #[test]
178    fn test_build_sourcehut_permalink_with_single_line_selection() {
179        let permalink = Sourcehut.build_permalink(
180            ParsedGitRemote {
181                owner: "zed-industries".into(),
182                repo: "zed".into(),
183            },
184            BuildPermalinkParams {
185                sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
186                path: "crates/editor/src/git/permalink.rs",
187                selection: Some(6..6),
188            },
189        );
190
191        let expected_url = "https://git.sr.ht/~zed-industries/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs#L7";
192        assert_eq!(permalink.to_string(), expected_url.to_string())
193    }
194
195    #[test]
196    fn test_build_sourcehut_permalink_with_multi_line_selection() {
197        let permalink = Sourcehut.build_permalink(
198            ParsedGitRemote {
199                owner: "zed-industries".into(),
200                repo: "zed".into(),
201            },
202            BuildPermalinkParams {
203                sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
204                path: "crates/editor/src/git/permalink.rs",
205                selection: Some(23..47),
206            },
207        );
208
209        let expected_url = "https://git.sr.ht/~zed-industries/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs#L24-48";
210        assert_eq!(permalink.to_string(), expected_url.to_string())
211    }
212}