gitow 1.0.0

Open Git remotes and repository pages in your browser from the CLI
Documentation
use crate::remote::ParsedRemote;

use super::types::ProviderKind;

#[derive(Debug, Clone)]
pub(super) struct ProviderContext {
    pub repo_url: String,
    pub console_repo_url: String,
    pub azure_workitems_url: Option<String>,
}

pub(super) fn detect_provider(remote: &ParsedRemote) -> ProviderKind {
    let path_segments: Vec<&str> = remote.url_path.split('/').collect();

    if remote.domain == "github.com" {
        ProviderKind::GitHub
    } else if remote.domain == "gist.github.com" {
        ProviderKind::Gist
    } else if remote.forge.as_deref() == Some("gitea") {
        ProviderKind::Gitea
    } else if remote.domain == "bitbucket.org" {
        ProviderKind::BitbucketCloud
    } else if path_segments.len() >= 3 && path_segments[path_segments.len() - 3] == "scm" {
        ProviderKind::BitbucketServer
    } else if path_segments.len() >= 2 && path_segments[path_segments.len() - 2] == "_git" {
        ProviderKind::AzureDevOps
    } else if remote.domain.ends_with("amazonaws.com") {
        ProviderKind::AwsCodeCommit
    } else if remote.domain.contains("gitlab") {
        ProviderKind::GitLab
    } else {
        ProviderKind::Generic
    }
}

pub(super) fn provider_context(remote: &ParsedRemote, kind: ProviderKind) -> ProviderContext {
    let path_segments: Vec<&str> = remote.url_path.split('/').collect();

    match kind {
        ProviderKind::BitbucketServer => bitbucket_server_context(remote, &path_segments),
        ProviderKind::AzureDevOps => azure_devops_context(remote),
        ProviderKind::AwsCodeCommit => aws_codecommit_context(remote),
        _ => default_context(remote),
    }
}

fn bitbucket_server_context(remote: &ParsedRemote, path_segments: &[&str]) -> ProviderContext {
    let prefix = &path_segments[..path_segments.len() - 3];
    let project = path_segments[path_segments.len() - 2];
    let repository = path_segments[path_segments.len() - 1];
    let mut rebuilt_segments = prefix
        .iter()
        .map(|segment| (*segment).to_string())
        .collect::<Vec<_>>();
    rebuilt_segments.push("projects".to_string());
    rebuilt_segments.push(project.to_string());
    rebuilt_segments.push("repos".to_string());
    rebuilt_segments.push(repository.to_string());
    let repo_path = rebuilt_segments.join("/");
    let repo_url = format!("{}://{}/{}", remote.protocol, remote.domain, repo_path);

    ProviderContext {
        repo_url: repo_url.clone(),
        console_repo_url: repo_url,
        azure_workitems_url: None,
    }
}

fn azure_devops_context(remote: &ParsedRemote) -> ProviderContext {
    let repo_url = format!(
        "{}://{}/{}",
        remote.protocol, remote.domain, remote.url_path
    );
    let azure_workitems_url = if let Some((prefix, _)) = remote.url_path.split_once("/_git/") {
        Some(format!(
            "{}://{}/{}/_workitems",
            remote.protocol, remote.domain, prefix
        ))
    } else if let Some(repository) = remote.url_path.strip_prefix("_git/") {
        Some(format!(
            "{}://{}/{}/_workitems",
            remote.protocol, remote.domain, repository
        ))
    } else {
        Some(format!(
            "{}://{}/{}/_workitems",
            remote.protocol, remote.domain, remote.url_path
        ))
    };

    ProviderContext {
        repo_url: repo_url.clone(),
        console_repo_url: repo_url,
        azure_workitems_url,
    }
}

fn aws_codecommit_context(remote: &ParsedRemote) -> ProviderContext {
    let region = remote
        .domain
        .split('.')
        .nth(1)
        .unwrap_or_default()
        .to_string();
    let repository = remote.url_path.rsplit('/').next().unwrap_or_default();
    let console_repo_url = format!(
        "https://{region}.console.aws.amazon.com/codecommit/home?region={region}#/repository/{repository}"
    );
    let repo_url = format!("{console_repo_url}/browse/");

    ProviderContext {
        repo_url,
        console_repo_url,
        azure_workitems_url: None,
    }
}

fn default_context(remote: &ParsedRemote) -> ProviderContext {
    let repo_url = format!(
        "{}://{}/{}",
        remote.protocol, remote.domain, remote.url_path
    );
    ProviderContext {
        repo_url: repo_url.clone(),
        console_repo_url: repo_url,
        azure_workitems_url: None,
    }
}