1use core::fmt;
2use std::{ops::Range, sync::Arc};
3
4use anyhow::Result;
5use url::Url;
6use util::{codeberg, github, http::HttpClient};
7
8use crate::Oid;
9
10#[derive(Clone, Debug, Hash)]
11pub enum HostingProvider {
12 Github,
13 Gitlab,
14 Gitee,
15 Bitbucket,
16 Sourcehut,
17 Codeberg,
18}
19
20impl HostingProvider {
21 pub(crate) fn base_url(&self) -> Url {
22 let base_url = match self {
23 Self::Github => "https://github.com",
24 Self::Gitlab => "https://gitlab.com",
25 Self::Gitee => "https://gitee.com",
26 Self::Bitbucket => "https://bitbucket.org",
27 Self::Sourcehut => "https://git.sr.ht",
28 Self::Codeberg => "https://codeberg.org",
29 };
30
31 Url::parse(&base_url).unwrap()
32 }
33
34 /// Returns the fragment portion of the URL for the selected lines in
35 /// the representation the [`GitHostingProvider`] expects.
36 pub(crate) fn line_fragment(&self, selection: &Range<u32>) -> String {
37 if selection.start == selection.end {
38 let line = selection.start + 1;
39
40 match self {
41 Self::Github | Self::Gitlab | Self::Gitee | Self::Sourcehut | Self::Codeberg => {
42 format!("L{}", line)
43 }
44 Self::Bitbucket => format!("lines-{}", line),
45 }
46 } else {
47 let start_line = selection.start + 1;
48 let end_line = selection.end + 1;
49
50 match self {
51 Self::Github | Self::Codeberg => format!("L{}-L{}", start_line, end_line),
52 Self::Gitlab | Self::Gitee | Self::Sourcehut => {
53 format!("L{}-{}", start_line, end_line)
54 }
55 Self::Bitbucket => format!("lines-{}:{}", start_line, end_line),
56 }
57 }
58 }
59
60 pub fn supports_avatars(&self) -> bool {
61 match self {
62 HostingProvider::Github | HostingProvider::Codeberg => true,
63 _ => false,
64 }
65 }
66
67 pub async fn commit_author_avatar_url(
68 &self,
69 repo_owner: &str,
70 repo: &str,
71 commit: Oid,
72 client: Arc<dyn HttpClient>,
73 ) -> Result<Option<Url>> {
74 Ok(match self {
75 HostingProvider::Github => {
76 let commit = commit.to_string();
77 github::fetch_github_commit_author(repo_owner, repo, &commit, &client)
78 .await?
79 .map(|author| -> Result<Url, url::ParseError> {
80 let mut url = Url::parse(&author.avatar_url)?;
81 url.set_query(Some("size=128"));
82 Ok(url)
83 })
84 .transpose()
85 }
86 HostingProvider::Codeberg => {
87 let commit = commit.to_string();
88 codeberg::fetch_codeberg_commit_author(repo_owner, repo, &commit, &client)
89 .await?
90 .map(|author| Url::parse(&author.avatar_url))
91 .transpose()
92 }
93 _ => Ok(None),
94 }?)
95 }
96}
97
98impl fmt::Display for HostingProvider {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 let name = match self {
101 HostingProvider::Github => "GitHub",
102 HostingProvider::Gitlab => "GitLab",
103 HostingProvider::Gitee => "Gitee",
104 HostingProvider::Bitbucket => "Bitbucket",
105 HostingProvider::Sourcehut => "Sourcehut",
106 HostingProvider::Codeberg => "Codeberg",
107 };
108 write!(f, "{}", name)
109 }
110}