1#![cfg_attr(test, deny(warnings))]
2#![warn(rust_2018_idioms)]
4#![allow(clippy::blacklisted_name)] #![allow(clippy::cognitive_complexity)] #![allow(clippy::derive_hash_xor_eq)] #![allow(clippy::explicit_into_iter_loop)] #![allow(clippy::explicit_iter_loop)] #![allow(clippy::identity_op)] #![allow(clippy::implicit_hasher)] #![allow(clippy::large_enum_variant)] #![allow(clippy::new_without_default)] #![allow(clippy::redundant_closure)] #![allow(clippy::redundant_closure_call)] #![allow(clippy::too_many_arguments)] #![allow(clippy::type_complexity)] #![allow(clippy::wrong_self_convention)] #![allow(clippy::write_with_newline)] #![allow(clippy::inefficient_to_string)] #![warn(clippy::needless_borrow)]
22#![warn(clippy::redundant_clone)]
23#![allow(clippy::trivially_copy_pass_by_ref)]
27#![allow(clippy::unneeded_field_pattern)]
29#![allow(clippy::identity_conversion)]
32
33use crate::core::shell::Verbosity::Verbose;
34use crate::core::Shell;
35use anyhow::Error;
36use log::debug;
37use serde::ser;
38use std::fmt;
39
40pub use crate::util::errors::{InternalError, VerboseError};
41pub use crate::util::{CargoResult, CliError, CliResult, Config};
42
43pub const CARGO_ENV: &str = "CARGO";
44
45#[macro_use]
46mod macros;
47
48pub mod core;
49pub mod ops;
50pub mod sources;
51pub mod util;
52
53pub struct CommitInfo {
54 pub short_commit_hash: String,
55 pub commit_hash: String,
56 pub commit_date: String,
57}
58
59pub struct CfgInfo {
60 pub commit_info: Option<CommitInfo>,
62 pub release_channel: String,
64}
65
66pub struct VersionInfo {
67 pub major: u8,
68 pub minor: u8,
69 pub patch: u8,
70 pub pre_release: Option<String>,
71 pub cfg_info: Option<CfgInfo>,
74}
75
76impl fmt::Display for VersionInfo {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 write!(f, "cargo {}.{}.{}", self.major, self.minor, self.patch)?;
79 if let Some(channel) = self.cfg_info.as_ref().map(|ci| &ci.release_channel) {
80 if channel != "stable" {
81 write!(f, "-{}", channel)?;
82 let empty = String::new();
83 write!(f, "{}", self.pre_release.as_ref().unwrap_or(&empty))?;
84 }
85 };
86
87 if let Some(ref cfg) = self.cfg_info {
88 if let Some(ref ci) = cfg.commit_info {
89 write!(f, " ({} {})", ci.short_commit_hash, ci.commit_date)?;
90 }
91 };
92 Ok(())
93 }
94}
95
96pub fn print_json<T: ser::Serialize>(obj: &T) {
97 let encoded = serde_json::to_string(&obj).unwrap();
98 println!("{}", encoded);
99}
100
101pub fn exit_with_error(err: CliError, shell: &mut Shell) -> ! {
102 debug!("exit_with_error; err={:?}", err);
103 if let Some(ref err) = err.error {
104 if let Some(clap_err) = err.downcast_ref::<clap::Error>() {
105 clap_err.exit()
106 }
107 }
108
109 let CliError { error, exit_code } = err;
110 if let Some(error) = error {
111 display_error(&error, shell);
112 }
113
114 std::process::exit(exit_code)
115}
116
117pub fn display_error(err: &Error, shell: &mut Shell) {
119 debug!("display_error; err={:?}", err);
120 let has_verbose = _display_error(err, shell);
121 if has_verbose {
122 drop(writeln!(
123 shell.err(),
124 "\nTo learn more, run the command again with --verbose."
125 ));
126 }
127 if err
128 .chain()
129 .any(|e| e.downcast_ref::<InternalError>().is_some())
130 {
131 drop(shell.note("this is an unexpected cargo internal error"));
132 drop(
133 shell.note(
134 "we would appreciate a bug report: https://github.com/rust-lang/cargo/issues/",
135 ),
136 );
137 drop(shell.note(format!("{}", version())));
138 }
141}
142
143fn _display_error(err: &Error, shell: &mut Shell) -> bool {
144 let verbosity = shell.verbosity();
145 let is_verbose = |e: &(dyn std::error::Error + 'static)| -> bool {
146 verbosity != Verbose && e.downcast_ref::<VerboseError>().is_some()
147 };
148 if is_verbose(err.as_ref()) {
150 return true;
151 }
152 drop(shell.error(&err));
153 for cause in err.chain().skip(1) {
154 if is_verbose(cause) {
157 return true;
158 }
159 drop(writeln!(shell.err(), "\nCaused by:"));
160 drop(writeln!(shell.err(), " {}", cause));
161 }
162 false
163}
164
165pub fn version() -> VersionInfo {
166 macro_rules! option_env_str {
167 ($name:expr) => {
168 option_env!($name).map(|s| s.to_string())
169 };
170 }
171
172 let major = 1;
187 let minor = env!("CARGO_PKG_VERSION_MINOR").parse::<u8>().unwrap() - 1;
188 let patch = env!("CARGO_PKG_VERSION_PATCH").parse::<u8>().unwrap();
189
190 match option_env!("CFG_RELEASE_CHANNEL") {
191 Some(_) => {
193 let commit_info = option_env!("CFG_COMMIT_HASH").map(|s| CommitInfo {
194 commit_hash: s.to_string(),
195 short_commit_hash: option_env_str!("CFG_SHORT_COMMIT_HASH").unwrap(),
196 commit_date: option_env_str!("CFG_COMMIT_DATE").unwrap(),
197 });
198 VersionInfo {
199 major,
200 minor,
201 patch,
202 pre_release: option_env_str!("CARGO_PKG_VERSION_PRE"),
203 cfg_info: Some(CfgInfo {
204 release_channel: option_env_str!("CFG_RELEASE_CHANNEL").unwrap(),
205 commit_info,
206 }),
207 }
208 }
209 None => VersionInfo {
211 major,
212 minor,
213 patch,
214 pre_release: option_env_str!("CARGO_PKG_VERSION_PRE"),
215 cfg_info: None,
216 },
217 }
218}