command_stream/execa/
result.rs1use std::fmt;
2use std::time::Duration;
3
4#[derive(Debug, Clone, Default)]
6pub struct ExecaResult {
7 pub stdout: Vec<u8>,
8 pub stderr: Vec<u8>,
9 pub all: Option<Vec<u8>>,
10 pub exit_code: Option<i32>,
11 pub signal: Option<String>,
12 pub failed: bool,
13 pub killed: bool,
14 pub timed_out: bool,
15 pub is_canceled: bool,
16 pub is_max_buffer: bool,
17 pub command: String,
18 pub escaped_command: String,
19 pub duration: Duration,
20 pub cause: Option<String>,
21}
22
23impl ExecaResult {
24 pub fn text(&self) -> String {
26 String::from_utf8_lossy(&self.stdout).into_owned()
27 }
28
29 pub fn stderr_text(&self) -> String {
30 String::from_utf8_lossy(&self.stderr).into_owned()
31 }
32
33 pub fn stdout_lines(&self) -> Vec<String> {
34 lines(&self.stdout)
35 }
36
37 pub fn stderr_lines(&self) -> Vec<String> {
38 lines(&self.stderr)
39 }
40}
41
42fn lines(bytes: &[u8]) -> Vec<String> {
43 if bytes.is_empty() {
44 return Vec::new();
45 }
46 String::from_utf8_lossy(bytes)
47 .split('\n')
48 .map(|line| line.strip_suffix('\r').unwrap_or(line).to_string())
49 .collect()
50}
51
52pub(crate) fn strip_final_newline(bytes: &mut Vec<u8>) {
53 if bytes.last() == Some(&b'\n') {
54 bytes.pop();
55 if bytes.last() == Some(&b'\r') {
56 bytes.pop();
57 }
58 } else if bytes.last() == Some(&b'\r') {
59 bytes.pop();
60 }
61}
62
63#[derive(Debug, Clone)]
65pub struct ExecaError {
66 pub result: Box<ExecaResult>,
67}
68
69impl fmt::Display for ExecaError {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 let result = &self.result;
72 let reason = if result.timed_out {
73 "timed out".to_string()
74 } else if result.is_canceled {
75 "canceled".to_string()
76 } else if result.is_max_buffer {
77 "exceeded max_buffer".to_string()
78 } else if let Some(cause) = &result.cause {
79 cause.clone()
80 } else if let Some(signal) = &result.signal {
81 format!("killed with {signal}")
82 } else {
83 format!("failed with exit code {}", result.exit_code.unwrap_or(-1))
84 };
85 write!(f, "Command {reason}: {}", result.command)
86 }
87}
88
89impl std::error::Error for ExecaError {}
90
91pub type ExecaOutcome = Result<ExecaResult, ExecaError>;