hosting_provider.rs

  1use std::{ops::Range, sync::Arc};
  2
  3use anyhow::Result;
  4use async_trait::async_trait;
  5use url::Url;
  6use util::http::HttpClient;
  7
  8use crate::hosting_providers::{Bitbucket, Codeberg, Gitee, Github, Gitlab, Sourcehut};
  9use crate::Oid;
 10
 11#[derive(Debug, PartialEq, Eq, Clone)]
 12pub struct PullRequest {
 13    pub number: u32,
 14    pub url: Url,
 15}
 16
 17pub struct BuildCommitPermalinkParams<'a> {
 18    pub sha: &'a str,
 19}
 20
 21pub struct BuildPermalinkParams<'a> {
 22    pub sha: &'a str,
 23    pub path: &'a str,
 24    pub selection: Option<Range<u32>>,
 25}
 26
 27/// A Git hosting provider.
 28#[async_trait]
 29pub trait GitHostingProvider {
 30    /// Returns the name of the provider.
 31    fn name(&self) -> String;
 32
 33    /// Returns the base URL of the provider.
 34    fn base_url(&self) -> Url;
 35
 36    /// Returns a permalink to a Git commit on this hosting provider.
 37    fn build_commit_permalink(
 38        &self,
 39        remote: &ParsedGitRemote,
 40        params: BuildCommitPermalinkParams,
 41    ) -> Url;
 42
 43    /// Returns a permalink to a file and/or selection on this hosting provider.
 44    fn build_permalink(&self, remote: ParsedGitRemote, params: BuildPermalinkParams) -> Url;
 45
 46    /// Returns whether this provider supports avatars.
 47    fn supports_avatars(&self) -> bool;
 48
 49    /// Returns a URL fragment to the given line selection.
 50    fn line_fragment(&self, selection: &Range<u32>) -> String {
 51        if selection.start == selection.end {
 52            let line = selection.start + 1;
 53
 54            self.format_line_number(line)
 55        } else {
 56            let start_line = selection.start + 1;
 57            let end_line = selection.end + 1;
 58
 59            self.format_line_numbers(start_line, end_line)
 60        }
 61    }
 62
 63    /// Returns a formatted line number to be placed in a permalink URL.
 64    fn format_line_number(&self, line: u32) -> String;
 65
 66    /// Returns a formatted range of line numbers to be placed in a permalink URL.
 67    fn format_line_numbers(&self, start_line: u32, end_line: u32) -> String;
 68
 69    fn parse_remote_url<'a>(&self, url: &'a str) -> Option<ParsedGitRemote<'a>>;
 70
 71    fn extract_pull_request(
 72        &self,
 73        _remote: &ParsedGitRemote,
 74        _message: &str,
 75    ) -> Option<PullRequest> {
 76        None
 77    }
 78
 79    async fn commit_author_avatar_url(
 80        &self,
 81        _repo_owner: &str,
 82        _repo: &str,
 83        _commit: Oid,
 84        _http_client: Arc<dyn HttpClient>,
 85    ) -> Result<Option<Url>> {
 86        Ok(None)
 87    }
 88}
 89
 90#[derive(Debug)]
 91pub struct ParsedGitRemote<'a> {
 92    pub owner: &'a str,
 93    pub repo: &'a str,
 94}
 95
 96pub fn parse_git_remote_url(
 97    url: &str,
 98) -> Option<(
 99    Arc<dyn GitHostingProvider + Send + Sync + 'static>,
100    ParsedGitRemote,
101)> {
102    let providers: Vec<Arc<dyn GitHostingProvider + Send + Sync + 'static>> = vec![
103        Arc::new(Github),
104        Arc::new(Gitlab),
105        Arc::new(Bitbucket),
106        Arc::new(Codeberg),
107        Arc::new(Gitee),
108        Arc::new(Sourcehut),
109    ];
110
111    providers.into_iter().find_map(|provider| {
112        provider
113            .parse_remote_url(&url)
114            .map(|parsed_remote| (provider, parsed_remote))
115    })
116}