    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95

    use crate::core::{Shell, Workspace};
    use crate::ops;
    use crate::util::config::{Config, PathAndArgs};
    use crate::util::CargoResult;
    use std::path::Path;
    use std::path::PathBuf;
    use std::process::Command;

    /// Strongly typed options for the `cargo doc` command.
    #[derive(Debug)]
    pub struct DocOptions {
        /// Whether to attempt to open the browser after compiling the docs
        pub open_result: bool,
        /// Options to pass through to the compiler
        pub compile_opts: ops::CompileOptions,
    }

    /// Main method for `cargo doc`.
    pub fn doc(ws: &Workspace<'_>, options: &DocOptions) -> CargoResult<()> {
        let compilation = ops::compile(ws, &options.compile_opts)?;

        if options.open_result {
            let name = &compilation
                .root_crate_names
                .get(0)
                .ok_or_else(|| anyhow::anyhow!("no crates with documentation"))?;
            let kind = options.compile_opts.build_config.single_requested_kind()?;
            let path = compilation.root_output[&kind]
                .with_file_name("doc")
                .join(&name)
                .join("index.html");
            if path.exists() {
                let config_browser = {
                    let cfg: Option<PathAndArgs> = ws.config().get("doc.browser")?;
                    cfg.map(|path_args| (path_args.path.resolve_program(ws.config()), path_args.args))
                };
                let mut shell = ws.config().shell();
                let link = shell.err_file_hyperlink(&path);
                shell.status(
                    "Opening",
                    format!("{}{}{}", link.open(), path.display(), link.close()),
                )?;
                open_docs(&path, &mut shell, config_browser, ws.config())?;
            }
        } else {
            for name in &compilation.root_crate_names {
                for kind in &options.compile_opts.build_config.requested_kinds {
                    let path = compilation.root_output[&kind]
                        .with_file_name("doc")
                        .join(&name)
                        .join("index.html");
                    if path.exists() {
                        let mut shell = ws.config().shell();
                        let link = shell.err_file_hyperlink(&path);
                        shell.status(
                            "Generated",
                            format!("{}{}{}", link.open(), path.display(), link.close()),
                        )?;
                    }
                }
            }
        }

        Ok(())
    }

    fn open_docs(
        path: &Path,
        shell: &mut Shell,
        config_browser: Option<(PathBuf, Vec<String>)>,
        config: &Config,
    ) -> CargoResult<()> {
        let browser =
            config_browser.or_else(|| Some((PathBuf::from(config.get_env_os("BROWSER")?), Vec::new())));

        match browser {
            Some((browser, initial_args)) => {
                if let Err(e) = Command::new(&browser).args(initial_args).arg(path).status() {
                    shell.warn(format!(
                        "Couldn't open docs with {}: {}",
                        browser.to_string_lossy(),
                        e
                    ))?;
                }
            }
            None => {
                if let Err(e) = opener::open(&path) {
                    let e = e.into();
                    crate::display_warning_with_error("couldn't open docs", &e, shell);
                }
            }
        };

        Ok(())
    }