]>
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.
59 // An alias with an empty argv can be created via `"empty-alias" = ""`.
60 let first
= argv
.get(0).map(String
::as_str
).unwrap_or(subcommand
);
63 None
=> subcommand
.to_string(),
66 let subcommand
= match check_builtin(&subcommand
) {
68 None
=> return Ok(false),
71 if resolve_executable(Path
::new("man")).is_ok() {
72 let man
= match extract_man(&subcommand
, "1") {
74 None
=> return Ok(false),
76 write_and_spawn(&subcommand
, &man
, "man")?
;
78 let txt
= match extract_man(&subcommand
, "txt") {
80 None
=> return Ok(false),
82 if resolve_executable(Path
::new("less")).is_ok() {
83 write_and_spawn(&subcommand
, &txt
, "less")?
;
84 } else if resolve_executable(Path
::new("more")).is_ok() {
85 write_and_spawn(&subcommand
, &txt
, "more")?
;
87 drop(std
::io
::stdout().write_all(&txt
));
93 /// Checks if the given subcommand is an alias.
95 /// Returns None if it is not an alias.
96 fn check_alias(config
: &Config
, subcommand
: &str) -> Option
<Vec
<String
>> {
97 aliased_command(config
, subcommand
).ok().flatten()
100 /// Checks if the given subcommand is a built-in command (not via an alias).
102 /// Returns None if it is not a built-in command.
103 fn check_builtin(subcommand
: &str) -> Option
<&str> {
104 super::builtin_exec(subcommand
).map(|_
| subcommand
)
107 /// Extracts the given man page from the compressed archive.
109 /// Returns None if the command wasn't found.
110 fn extract_man(subcommand
: &str, extension
: &str) -> Option
<Vec
<u8>> {
111 let extract_name
= OsString
::from(format
!("cargo-{}.{}", subcommand
, extension
));
112 let gz
= GzDecoder
::new(COMPRESSED_MAN
);
113 let mut ar
= tar
::Archive
::new(gz
);
114 // Unwraps should be safe here, since this is a static archive generated
115 // by our build script. It should never be an invalid format!
116 for entry
in ar
.entries().unwrap() {
117 let mut entry
= entry
.unwrap();
118 let path
= entry
.path().unwrap();
119 if path
.file_name().unwrap() != extract_name
{
122 let mut result
= Vec
::new();
123 entry
.read_to_end(&mut result
).unwrap();
129 /// Write the contents of a man page to disk and spawn the given command to
131 fn write_and_spawn(name
: &str, contents
: &[u8], command
: &str) -> CargoResult
<()> {
132 let prefix
= format
!("cargo-{}.", name
);
133 let mut tmp
= tempfile
::Builder
::new().prefix(&prefix
).tempfile()?
;
134 let f
= tmp
.as_file_mut();
135 f
.write_all(contents
)?
;
137 let path
= tmp
.path();
138 // Use a path relative to the temp directory so that it can work on
139 // cygwin/msys systems which don't handle windows-style paths.
140 let mut relative_name
= std
::ffi
::OsString
::from("./");
141 relative_name
.push(path
.file_name().unwrap());
142 let mut cmd
= std
::process
::Command
::new(command
)
144 .current_dir(path
.parent().unwrap())