iai_callgrind_runner/
error.rs

1use std::fmt::Display;
2use std::io::stderr;
3use std::os::unix::process::ExitStatusExt;
4use std::path::PathBuf;
5use std::process::{ExitStatus, Output};
6
7use version_compare::Cmp;
8
9use crate::runner::common::ModulePath;
10use crate::runner::tool::{ToolOutputPath, ValgrindTool};
11use crate::util::write_all_to_stderr;
12
13#[derive(Debug, PartialEq, Clone)]
14pub enum Error {
15    InitError(String),
16    VersionMismatch(version_compare::Cmp, String, String),
17    LaunchError(PathBuf, String),
18    /// (`process_name`, [`Output`], [`ExitStatus`], [`ToolOutputPath`])
19    ProcessError((String, Option<Output>, ExitStatus, Option<ToolOutputPath>)),
20    InvalidBoolArgument((String, String)),
21    ParseError((PathBuf, String)),
22    RegressionError(bool),
23    EnvironmentVariableError((String, String)),
24    SandboxError(String),
25    BenchmarkError(ValgrindTool, ModulePath, String),
26}
27
28impl std::error::Error for Error {}
29
30impl Display for Error {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        match self {
33            Self::InitError(message) => {
34                let runner_version = env!("CARGO_PKG_VERSION").to_owned();
35                write!(
36                    f,
37                    "Failed to initialize iai-callgrind-runner: {message}\n\nDetected version of \
38                     iai-callgrind-runner is {runner_version}. This error can be caused by a \
39                     version mismatch between iai-callgrind and iai-callgrind-runner. If you \
40                     updated the library (iai-callgrind) in your Cargo.toml file, the binary \
41                     (iai-callgrind-runner) needs to be updated to the same version and vice \
42                     versa."
43                )
44            }
45            Self::VersionMismatch(cmp, runner_version, library_version) => match cmp {
46                Cmp::Lt => write!(
47                    f,
48                    "iai-callgrind-runner ({runner_version}) is older than iai-callgrind \
49                     ({library_version}). Please update iai-callgrind-runner by calling 'cargo \
50                     install --version {library_version} iai-callgrind-runner'"
51                ),
52                Cmp::Gt => write!(
53                    f,
54                    "iai-callgrind-runner ({runner_version}) is newer than iai-callgrind \
55                     ({library_version}). Please update iai-callgrind to '{runner_version}' in \
56                     your Cargo.toml file"
57                ),
58                Cmp::Ne => write!(
59                    f,
60                    "No version information found for iai-callgrind but iai-callgrind-runner \
61                     ({runner_version}) is >= '0.3.0'. Please update iai-callgrind to \
62                     '{runner_version}' in your Cargo.toml file"
63                ),
64                _ => unreachable!(),
65            },
66            Self::LaunchError(exec, message) => {
67                write!(f, "Error launching '{}': {message}", exec.display())
68            }
69            Self::ProcessError((process, output, status, output_path)) => {
70                if let Some(output_path) = output_path {
71                    output_path
72                        .dump_log(log::Level::Error, &mut stderr())
73                        .expect("Printing error output should succeed");
74                }
75                if let Some(output) = output {
76                    write_all_to_stderr(&output.stderr);
77                }
78
79                if let Some(code) = status.code() {
80                    write!(f, "Error running '{process}': Exit code was: '{code}'")
81                } else if let Some(signal) = status.signal() {
82                    write!(
83                        f,
84                        "Error running '{process}': Terminated by a signal '{signal}'"
85                    )
86                } else {
87                    write!(f, "Error running '{process}': Terminated abnormally")
88                }
89            }
90            Self::InvalidBoolArgument((option, value)) => {
91                write!(
92                    f,
93                    "Invalid argument for {option}: '{value}'. Valid values are 'yes' or 'no'"
94                )
95            }
96            Self::ParseError((path, message)) => {
97                write!(f, "Error parsing file '{}': {message}", path.display())
98            }
99            Self::RegressionError(is_fatal) => {
100                if *is_fatal {
101                    write!(f, "Performance has regressed. Aborting ...",)
102                } else {
103                    write!(f, "Performance has regressed.",)
104                }
105            }
106            Self::EnvironmentVariableError((var, reason)) => {
107                write!(f, "Failed parsing environment variable {var}: {reason}")
108            }
109            Self::SandboxError(message) => {
110                write!(f, "Error in sandbox: {message}")
111            }
112            Self::BenchmarkError(tool, module_path, message) => {
113                write!(f, "Error in {tool} benchmark {module_path}: {message}")
114            }
115        }
116    }
117}