Skip to main content

gor/cmd/
browse.rs

1//! Implementation of the `gor browse` subcommand.
2//!
3//! Opens a repository, branch, commit, issue, PR, or other resource
4//! in the default web browser.
5
6#![allow(clippy::print_stdout, clippy::print_stderr)]
7
8use crate::cli::BrowseCommand;
9use crate::repository::{detect_remote, parse_repo_spec};
10use anyhow::Context;
11
12/// Run the `gor browse` subcommand.
13///
14/// # Errors
15///
16/// Returns an error if the repository cannot be determined.
17pub fn run(cmd: BrowseCommand, hostname: Option<&str>) -> anyhow::Result<()> {
18    let spec = match cmd.repo.as_deref() {
19        Some(s) => parse_repo_spec(s).context("invalid repository spec")?,
20        None => detect_remote().ok_or_else(|| {
21            anyhow::anyhow!(
22                "could not detect repository from current directory; specify OWNER/REPO with --repo"
23            )
24        })?,
25    };
26
27    let host = hostname.unwrap_or("github.com");
28    let base_url = format!("https://{host}/{}/{}", spec.owner, spec.repo);
29
30    let url = if let Some(issue) = cmd.issue {
31        format!("{base_url}/issues/{issue}")
32    } else if let Some(pr) = cmd.pr {
33        format!("{base_url}/pull/{pr}")
34    } else if let Some(branch) = cmd.branch {
35        format!("{base_url}/tree/{branch}")
36    } else if let Some(commit) = cmd.commit {
37        format!("{base_url}/commit/{commit}")
38    } else if cmd.projects {
39        format!("{base_url}/projects")
40    } else if cmd.wiki {
41        format!("{base_url}/wiki")
42    } else if cmd.settings {
43        format!("{base_url}/settings")
44    } else {
45        base_url
46    };
47
48    open_in_browser(&url);
49    println!("Opening {url}");
50    Ok(())
51}
52
53/// Open a URL in the default browser.
54pub fn open_in_browser(url: &str) {
55    #[cfg(target_os = "linux")]
56    {
57        let _ = std::process::Command::new("xdg-open").arg(url).spawn();
58    }
59    #[cfg(target_os = "macos")]
60    {
61        let _ = std::process::Command::new("open").arg(url).spawn();
62    }
63    #[cfg(target_os = "windows")]
64    {
65        let _ = std::process::Command::new("cmd")
66            .args(["/c", "start", url])
67            .spawn();
68    }
69    #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
70    {
71        println!("Open {url} in your browser");
72    }
73}
74
75#[cfg(test)]
76#[allow(clippy::expect_used)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn open_in_browser_does_not_panic() {
82        open_in_browser("https://github.com/octocat/hello-world");
83    }
84}