]>
git.proxmox.com Git - cargo.git/blob - src/bin/cargo/commands/help.rs
1 use crate::aliased_command
;
2 use cargo
::util
::errors
::CargoResult
;
3 use cargo
::{drop_println, Config}
;
4 use cargo_util
::paths
::resolve_executable
;
5 use flate2
::read
::GzDecoder
;
6 use std
::ffi
::OsString
;
11 const COMPRESSED_MAN
: &[u8] = include_bytes
!(concat
!(env
!("OUT_DIR"), "/man.tgz"));
13 /// Checks if the `help` command is being issued.
15 /// This runs before clap processing, because it needs to intercept the `help`
16 /// command if a man page is available.
18 /// Returns `true` if help information was successfully displayed to the user.
19 /// In this case, Cargo should exit.
20 pub fn handle_embedded_help(config
: &Config
) -> bool
{
21 match try_help(config
) {
25 log
::warn
!("help failed: {:?}", e
);
31 fn try_help(config
: &Config
) -> CargoResult
<bool
> {
32 let mut args
= std
::env
::args_os()
34 .skip_while(|arg
| arg
.to_str().map_or(false, |s
| s
.starts_with('
-'
)));
37 .map_or(false, |arg
| arg
.to_str() == Some("help"))
41 let subcommand
= match args
.next() {
43 None
=> return Ok(false),
45 let subcommand
= match subcommand
.to_str() {
47 None
=> return Ok(false),
50 let subcommand
= match check_alias(config
, subcommand
) {
51 // If this alias is more than a simple subcommand pass-through, show the alias.
52 Some(argv
) if argv
.len() > 1 => {
53 let alias
= argv
.join(" ");
54 drop_println
!(config
, "`{}` is aliased to `{}`", subcommand
, alias
);
57 // Otherwise, resolve the alias into its subcommand.
58 Some(argv
) => argv
[0].clone(),
59 None
=> subcommand
.to_string(),
62 let subcommand
= match check_builtin(&subcommand
) {
64 None
=> return Ok(false),
67 if resolve_executable(Path
::new("man")).is_ok() {
68 let man
= match extract_man(&subcommand
, "1") {
70 None
=> return Ok(false),
72 write_and_spawn(&subcommand
, &man
, "man")?
;
74 let txt
= match extract_man(&subcommand
, "txt") {
76 None
=> return Ok(false),
78 if resolve_executable(Path
::new("less")).is_ok() {
79 write_and_spawn(&subcommand
, &txt
, "less")?
;
80 } else if resolve_executable(Path
::new("more")).is_ok() {
81 write_and_spawn(&subcommand
, &txt
, "more")?
;
83 drop(std
::io
::stdout().write_all(&txt
));
89 /// Checks if the given subcommand is an alias.
91 /// Returns None if it is not an alias.
92 fn check_alias(config
: &Config
, subcommand
: &str) -> Option
<Vec
<String
>> {
93 aliased_command(config
, subcommand
).ok().flatten()
96 /// Checks if the given subcommand is a built-in command (not via an alias).
98 /// Returns None if it is not a built-in command.
99 fn check_builtin(subcommand
: &str) -> Option
<&str> {
100 super::builtin_exec(subcommand
).map(|_
| subcommand
)
103 /// Extracts the given man page from the compressed archive.
105 /// Returns None if the command wasn't found.
106 fn extract_man(subcommand
: &str, extension
: &str) -> Option
<Vec
<u8>> {
107 let extract_name
= OsString
::from(format
!("cargo-{}.{}", subcommand
, extension
));
108 let gz
= GzDecoder
::new(COMPRESSED_MAN
);
109 let mut ar
= tar
::Archive
::new(gz
);
110 // Unwraps should be safe here, since this is a static archive generated
111 // by our build script. It should never be an invalid format!
112 for entry
in ar
.entries().unwrap() {
113 let mut entry
= entry
.unwrap();
114 let path
= entry
.path().unwrap();
115 if path
.file_name().unwrap() != extract_name
{
118 let mut result
= Vec
::new();
119 entry
.read_to_end(&mut result
).unwrap();
125 /// Write the contents of a man page to disk and spawn the given command to
127 fn write_and_spawn(name
: &str, contents
: &[u8], command
: &str) -> CargoResult
<()> {
128 let prefix
= format
!("cargo-{}.", name
);
129 let mut tmp
= tempfile
::Builder
::new().prefix(&prefix
).tempfile()?
;
130 let f
= tmp
.as_file_mut();
131 f
.write_all(contents
)?
;
133 let path
= tmp
.path();
134 // Use a path relative to the temp directory so that it can work on
135 // cygwin/msys systems which don't handle windows-style paths.
136 let mut relative_name
= std
::ffi
::OsString
::from("./");
137 relative_name
.push(path
.file_name().unwrap());
138 let mut cmd
= std
::process
::Command
::new(command
)
140 .current_dir(path
.parent().unwrap())