1use colored::*;
2use shells::sh;
3use std::env;
4use std::error::Error;
5use std::fmt;
6
7#[derive(Debug)]
9pub struct ShellError<'a> {
10 command: String,
11 exit_code: i32,
12 stderr: String,
13 stdout: String,
14 error_id: &'a str,
15}
16
17impl ShellError<'_> {
18 pub fn new(
20 command: String,
21 exit_code: i32,
22 stderr: String,
23 stdout: String,
24 error_id: &'static str,
25 ) -> Self {
26 ShellError {
27 command,
28 exit_code,
29 stderr,
30 stdout,
31 error_id,
32 }
33 }
34}
35
36impl fmt::Display for ShellError<'_> {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 write!(
39 f,
40 "{}: {}\nExit code: {}\nError ID: {}\n",
41 "Command failed".red(),
42 self.command,
43 self.exit_code,
44 self.error_id.green(),
45 )?;
46
47 if !self.stdout.is_empty() {
48 write!(f, "Standard output:\n{}\n", self.stdout.green())?;
49 }
50
51 if !self.stderr.is_empty() {
52 write!(f, "Standard error:\n{}\n", self.stderr.magenta())?;
53 }
54
55 Ok(())
56 }
57}
58
59impl Error for ShellError<'_> {}
60
61pub fn execute_command(cmd: &str, error_id: &'static str) -> Result<String, ShellError<'static>> {
63 let command = cmd.to_string();
64 let (code, stdout, stderr) = sh!("{}", cmd);
65
66 if code == 0 {
68 Ok(stdout)
69 } else {
70 let error = ShellError::new(command, code, stderr, stdout, error_id);
71 Err(error)
72 }
73}
74
75pub fn get_env(env: &str, error_id: &'static str) -> Result<String, ShellError<'static>> {
76 match env::var(env) {
78 Err(e) => Err(ShellError {
79 command: format!("shell-exec: get_env({env})"),
80 exit_code: 0,
81 stderr: format!("Environment variable '{env}' is not defined: {e:#?}."),
82 stdout: "".to_string(),
83 error_id,
84 }),
85 Ok(value) => Ok(value),
86 }
87}
88
89pub fn main_run(run: fn() -> Result<(), Box<dyn Error>>) {
90 if let Err(e) = run() {
91 eprintln!("Version: {}", env!("CARGO_PKG_VERSION"));
92 eprintln!("Name: {}", env!("CARGO_PKG_NAME"));
93 eprintln!("Authors: {}", env!("CARGO_PKG_AUTHORS"));
94
95 eprintln!("Description: {}", env!("CARGO_PKG_DESCRIPTION"));
97 eprintln!("Homepage: {}", env!("CARGO_PKG_HOMEPAGE"));
98 eprintln!("Repository: {}", env!("CARGO_PKG_REPOSITORY"));
99 eprintln!("{e}")
100 }
101}
102
103#[macro_export]
110macro_rules! trap_panics_and_errors {
111 ($error_id:literal , $main:expr) => {
112 use std::process;
113 use std::error::Error;
114 use colored::*;
115 match std::panic::catch_unwind(|| {
116 match $main() {
117 Err(e) => {
118 eprintln!("{}: {}", "trap_panics_and_errors".red(), $error_id.green());
119 eprintln!(" Version: {}", env!("CARGO_PKG_VERSION"));
120 eprintln!(" Name: {}", env!("CARGO_PKG_NAME"));
121 eprintln!(" Authors: {}", env!("CARGO_PKG_AUTHORS"));
122
123 eprintln!(" Description: {}", env!("CARGO_PKG_DESCRIPTION"));
125 eprintln!(" Homepage: {}", env!("CARGO_PKG_HOMEPAGE"));
126 eprintln!(" Repository: {}", env!("CARGO_PKG_REPOSITORY"));
127 eprintln!(" Error: {e}");
128 process::exit(1)
130 }
131 Ok(result) => result,
132 }
133 }) {
134 Ok(result) => result,
135 Err(e) => {
136 eprintln!(
137 "Error id: {}, 31963-28837-7387. Error {}: {e:#?}!", $error_id,
138 "Application panicked".red()
139 );
140 std::process::exit(101);
141 }
142 }
143 };
144}
145
146#[macro_export]
147macro_rules! exec {
148 ($error_id:literal , $verbose:expr , $($cmd:tt )* ) => {{
149 let formatted_str = &format!($( $cmd )*);
150 if $verbose { eprintln!("{}", format!("exec!({},{})", $error_id, formatted_str ).magenta()) }
151 execute_command(formatted_str, $error_id)
152 }};
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn test_successful_command() {
161 let output = execute_command("echo Hello World", "8923-2323-2323").unwrap();
162 assert_eq!(output.trim(), "Hello World");
163 }
164
165 #[test]
166 fn test_successful_fmt() {
167 let output = exec!("8923-2323-2323", false, "echo Hello World").unwrap();
168 assert_eq!(output.trim(), "Hello World");
169 }
170
171 #[test]
172 fn test_successful_fmt2() {
173 let output = exec!("21236-28986-4446", true, "echo {}", "Hello World",).unwrap();
174 assert_eq!(output.trim(), "Hello World");
175 }
176
177 #[test]
178 fn test_failing_command() {
179 let result = execute_command("nonexistent_command", "8923-2323-3289");
180 assert!(result.is_err());
181 let error = result.unwrap_err();
182 assert_eq!(error.exit_code, 127);
183 assert!(!error.stderr.is_empty());
184 }
185}