hosting_provider.rs

  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}