1use colored::*;
2use shells::sh;
3use std::env;
4use std::error::Error;
5use std::fmt;
6
7#[derive(Debug, Clone)]
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<'_> {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 write!(
41 f,
42 "{}: '{}'\nsh_exec Exit code: {}\nsh_exec Error ID: {}\n",
43 "Command failed".red(),
44 self.command,
45 self.exit_code,
46 self.error_id.green(),
47 )?;
48
49 if !self.stdout.is_empty() {
50 write!(f, "Standard output:\n{}\n", self.stdout.green())?;
51 }
52
53 if !self.stderr.is_empty() {
54 write!(f, "Standard error:\n{}\n", self.stderr.magenta())?;
55 }
56
57 Ok(())
58 }
59}
60
61impl Error for ShellError<'_> {}
62
63pub fn execute_command(cmd: &str, error_id: &'static str) -> Result<String, ShellError<'static>> {
65 let command = cmd.to_string();
66 let (code, stdout, stderr) = sh!("{}", cmd);
67
68 if code == 0 {
70 Ok(stdout)
71 } else {
72 let error = ShellError::new(command, code, stderr, stdout, error_id);
73 Err(error)
74 }
75}
76
77pub fn get_env(env: &str, error_id: &'static str) -> Result<String, ShellError<'static>> {
80 match env::var(env) {
82 Err(e) => Err(ShellError {
83 command: format!("shell-exec: get_env({env})"),
84 exit_code: 0,
85 stderr: format!("Environment variable '{env}' is not defined: {e:#?}."),
86 stdout: "".to_string(),
87 error_id,
88 }),
89 Ok(value) => Ok(value),
90 }
91}
92
93pub fn main_run(run: fn() -> Result<(), Box<dyn Error>>) {
97 if let Err(e) = run() {
98 eprintln!("Version: {}", env!("CARGO_PKG_VERSION"));
99 eprintln!("Name: {}", env!("CARGO_PKG_NAME"));
100 eprintln!("Authors: {}", env!("CARGO_PKG_AUTHORS"));
101
102 eprintln!("Description: {}", env!("CARGO_PKG_DESCRIPTION"));
104 eprintln!("Homepage: {}", env!("CARGO_PKG_HOMEPAGE"));
105 eprintln!("Repository: {}", env!("CARGO_PKG_REPOSITORY"));
106 eprintln!("{e}")
107 }
108}
109
110#[macro_export]
117macro_rules! trap_panics_and_errors {
118 ($error_id:literal , $main:expr) => {
119 use std::process;
120 use std::error::Error;
121 use colored::*;
122 use log::*;
123 match std::panic::catch_unwind(|| {
124 match $main() {
125 Err(e) => {
126 error!("{}: {}", "trap_panics_and_errors".red(), $error_id.green());
127 error!(" Version: {}", env!("CARGO_PKG_VERSION"));
128 error!(" Name: {}", env!("CARGO_PKG_NAME"));
129 error!(" Authors: {}", env!("CARGO_PKG_AUTHORS"));
130
131 error!(" Description: {}", env!("CARGO_PKG_DESCRIPTION"));
133 error!(" Homepage: {}", env!("CARGO_PKG_HOMEPAGE"));
134 error!(" Repository: {}", env!("CARGO_PKG_REPOSITORY"));
135 error!(" Error: {e}");
136 process::exit(1)
138 }
139 Ok(result) => result,
140 }
141 }) {
142 Ok(result) => result,
143 Err(e) => {
144 eprintln!(
145 "Error id: {}, 31963-28837-7387. Error {}: {e:#?}!", $error_id,
146 "Application panicked".red()
147 );
148 std::process::exit(101);
149 }
150 }
151 };
152}
153
154#[macro_export]
158macro_rules! exec {
159 ($error_id:literal , $verbose:expr , $($cmd:tt )* ) => {{
160 let formatted_str = &format!($( $cmd )*);
161 if $verbose { eprintln!("{}", format!("exec!({},{})", $error_id, formatted_str ).magenta()) }
162 execute_command(formatted_str, $error_id)
163 }};
164}
165
166#[macro_export]
173macro_rules! s {
174 ($error_id:literal , $($cmd:tt )* ) => {{
175 let formatted_str = &format!($( $cmd )*);
176 info!("{}", format!("s!({},{})", $error_id, formatted_str ).magenta());
177 let output = execute_command(formatted_str, $error_id);
178 match output.clone() {
180 Ok(output) => debug!("{}", output),
181 Err(e) => error!("{}", e),
182 }
183 output
184 }};
185}
186
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
193 fn test_successful_command() {
194 let output = execute_command("echo Hello World", "8923-2323-2323").unwrap();
195 assert_eq!(output.trim(), "Hello World");
196 }
197
198 #[test]
199 fn test_successful_fmt() {
200 let output = exec!("8923-2323-2323", false, "echo Hello World").unwrap();
201 assert_eq!(output.trim(), "Hello World");
202 }
203
204 #[test]
205 fn test_successful_fmt2() {
206 let output = exec!("21236-28986-4446", true, "echo {}", "Hello World",).unwrap();
207 assert_eq!(output.trim(), "Hello World");
208 }
209
210 #[test]
211 fn test_failing_command() {
212 let result = execute_command("nonexistent_command", "8923-2323-3289");
213 assert!(result.is_err());
214 let error = result.unwrap_err();
215 assert_eq!(error.exit_code, 127);
216 assert!(!error.stderr.is_empty());
217 }
218}