ea_command/commands/
run.rs1use crate::archive;
2use crate::interface::Style;
3use crate::parsers;
4use atty;
5use pty::fork::Fork;
6use std;
7use std::error;
8use std::fmt;
9use std::fs;
10use std::io::{self, Read, Write};
11use std::process;
12
13#[derive(Debug)]
14enum RunError {
15 CouldNotExecuteCommand(String),
16 CommandEncounteredError(String),
17 CommandWasInterrupted(String),
18}
19
20impl RunError {
21 fn new(code: u8, command: &str) -> Option<Self> {
22 match code {
23 0 => Some(Self::CouldNotExecuteCommand(command.to_string())),
24 1 => Some(Self::CommandEncounteredError(command.to_string())),
25 2 => Some(Self::CommandWasInterrupted(command.to_string())),
26 _ => None,
27 }
28 }
29
30 fn could_not_execute_command() -> u8 { 0 }
31 fn command_encountered_error() -> u8 { 1 }
32 fn command_was_interrupted() -> u8 { 2 }
33}
34
35impl fmt::Display for RunError {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 match self {
38 RunError::CouldNotExecuteCommand(command) => write!(f, "could not execute {}", command),
39 RunError::CommandEncounteredError(command) => write!(f, "{} encountered an error", command),
40 RunError::CommandWasInterrupted(command) => write!(f, "{} was interrupted by signal", command),
41 }
42 }
43}
44
45impl error::Error for RunError {}
46
47static ERROR_SIGNAL: [u8; 4] = [0xde, 0xad, 0xbe, 0xaf];
49
50fn format_error(is_tty: bool, error: Box<dyn error::Error>) -> String {
51 if is_tty {
52 format!("\x1b[0m\x1b[31m[ea]: {}\x1b[0m\n", error)
53 } else {
54 format!("[ea]: {}\n", error)
55 }
56}
57
58pub fn run(style: &Style, executable: &str, arguments: &[String], debug: Option<String>) {
59 let is_tty = atty::is(atty::Stream::Stdout);
60 let mut output = execute(is_tty, executable, arguments);
61 let output_len = output.len();
62 let mut error_exit_code: Option<i32> = None;
63 if output_len >= 4 && output[(output_len - 4)..] == ERROR_SIGNAL {
64 _ = io::stderr().write(&output);
65 let error = RunError::new(output[output_len - 5], executable).expect("Error synthesized");
66 _ = io::stderr().write(format_error(is_tty, Box::new(error)).as_bytes());
67 error_exit_code = Some(output[output_len - 6] as i32);
68 output = output[0..(output_len - 6)].to_vec();
69 }
70
71 let parsed = match style {
72 Style::Grouped => parsers::grouped::grouped,
73 Style::Linear => parsers::linear::linear,
74 Style::Search => parsers::search::search,
75 Style::Rust => parsers::rust::rust,
76 Style::Py => parsers::python::python,
77 }(&output);
78
79 let (display, locations) = match parsed {
80 Ok(result) => result,
81 Err(error) => {
82 _ = io::stdout().write(&output);
83 _ = io::stderr().write(format_error(is_tty, Box::new(error)).as_bytes());
84 return;
85 }
86 };
87
88 if error_exit_code.is_none() || !&locations.is_empty() {
89 _ = io::stdout().write(&display);
90 _ = archive::write(&locations);
91 } if let Some(debug_path) = debug {
94 _ = fs::write(
95 format!("{}.args", debug_path),
96 format!("{:?}\n{}\n{:?}", style, executable, arguments),
97 );
98 _ = fs::write(format!("{}.in", debug_path), output);
99 _ = fs::write(format!("{}.out", debug_path), &display);
100 }
101
102 if let Some(code) = error_exit_code {
103 process::exit(code)
104 }
105}
106
107fn execute_simple(executable: &str, arguments: &[String], output: &mut Vec<u8>) -> i32 {
108 let execution_result = process::Command::new(executable).args(arguments).status();
111
112 let error_code: u8;
113 let exit: u8;
114 match execution_result {
115 Ok(exit_status) => {
116 if let Some(exit_code) = exit_status.code() {
117 exit = exit_code as u8;
118 error_code = RunError::command_encountered_error();
120 } else {
121 exit = 1;
122 error_code = RunError::command_was_interrupted();
123 }
124 }
125 Err(error) => {
126 output.extend_from_slice(error.to_string().as_bytes());
127 exit = error.raw_os_error().unwrap_or(1) as u8;
128 error_code = RunError::could_not_execute_command();
129 }
130 }
131
132 if exit != 0 {
137 output.push(exit);
138 output.push(error_code);
139 output.extend_from_slice(&ERROR_SIGNAL);
140 }
141
142 exit as i32
143}
144
145fn execute(is_tty: bool, executable: &str, arguments: &[String]) -> Vec<u8> {
146 let mut output = Vec::new();
147 if is_tty {
148 let fork = Fork::from_ptmx().unwrap();
149 if let Ok(mut parent) = fork.is_parent() {
150 _ = parent.read_to_end(&mut output);
151 } else {
152 let code = execute_simple(executable, arguments, &mut output);
153 if code != 0 {
154 _ = io::stderr().write(&output);
155 }
156 process::exit(code);
157 }
158 } else {
159 _ = execute_simple(executable, arguments, &mut output);
160 }
161 output
162}