Skip to main content

ctx_tui/
forge.rs

1//! Forge integrations: the PR-hosting service behind a checkout's remote.
2
3// Opens the current branch's PR in the browser via the forge's CLI, run in
4// the checkout; the CLI resolves the PR (or MR) and its URL itself.
5const GITHUB_PR_VIEW: &[&str] = &["gh", "pr", "view", "--web"];
6const GITLAB_PR_VIEW: &[&str] = &["glab", "mr", "view", "--web"];
7
8/// The PR-opening invocation for a checkout, derived from its remote URL.
9///
10/// The forge is per repo, not per installation, so it is read off the
11/// remote's host: GitLab hosts get glab, anything else defaults to gh.
12pub fn pr_view_command(remote_url: &str) -> Vec<String> {
13    let command = if host(remote_url).contains("gitlab") {
14        GITLAB_PR_VIEW
15    } else {
16        GITHUB_PR_VIEW
17    };
18    command.iter().map(|s| s.to_string()).collect()
19}
20
21/// The host of a remote URL, tolerating ssh/https/scp and local forms.
22fn host(url: &str) -> String {
23    let rest = url.split_once("://").map_or(url, |(_, r)| r);
24    let rest = rest.split_once('@').map_or(rest, |(_, r)| r);
25    rest.split(['/', ':'])
26        .next()
27        .unwrap_or_default()
28        .to_lowercase()
29}
30
31#[cfg(test)]
32mod tests {
33    use super::*;
34
35    #[test]
36    fn non_gitlab_remotes_default_to_gh() {
37        for url in [
38            "git@github.com:jane/tool.git",
39            "https://github.com/jane/tool.git",
40            "ssh://git@github.com/jane/tool.git",
41            "/local/mirrors/tool.git",
42        ] {
43            assert_eq!(pr_view_command(url), ["gh", "pr", "view", "--web"]);
44        }
45    }
46
47    #[test]
48    fn gitlab_remotes_use_glab() {
49        for url in [
50            "git@gitlab.com:jane/tool.git",
51            "https://gitlab.example.com/jane/tool.git",
52            "ssh://git@gitlab.com/jane/tool.git",
53        ] {
54            assert_eq!(pr_view_command(url), ["glab", "mr", "view", "--web"]);
55        }
56    }
57}