1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
use std::fmt::{Debug, Display, Formatter};
use std::process::{ExitStatus, Output};

use thiserror::Error;

use crate::Vec8ToString;

#[derive(Error, Clone, PartialEq, Eq, Debug)]
pub struct CmdError {
	pub status: Option<ExitStatus>,
	pub stdout: Vec<u8>,
	pub stderr: Vec<u8>,
}

impl From<Output> for CmdError {
	fn from(value: Output) -> Self {
		CmdError::from_err(value.status, value.stdout, value.stderr)
	}
}

impl From<Output> for crate::Error {
	fn from(value: Output) -> Self {
		crate::Error::CommandError(value.into())
	}
}

impl CmdError {
	pub fn from_err(status: ExitStatus, stdout: Vec<u8>, stderr: Vec<u8>) -> Self {
		CmdError {
			status: Some(status),
			stdout,
			stderr,
		}
	}

	pub fn from_status(status: ExitStatus) -> Self {
		CmdError {
			status: Some(status),
			stdout: vec![],
			stderr: vec![],
		}
	}

	pub fn from_str(msg: &str) -> Self {
		CmdError {
			status: None,
			stdout: vec![],
			stderr: msg.to_owned().into_bytes(),
		}
	}

	pub fn exit_code(&self) -> Option<i32> {
		match self.status {
			Some(s) => s.code(),
			None => None,
		}
	}
}

impl Display for CmdError {
	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
		if let Some(status) = self.status {
			if let Some(code) = status.code() {
				let _ = write!(f, "exit code: {}", code);
			} else {
				let _ = write!(f, "exit status: {}", self.status.unwrap_or(ExitStatus::default()));
			}
		} else {
			let _ = write!(f, "exit status: {}", self.status.unwrap_or(ExitStatus::default()));
		}

		if !self.stderr.is_empty() {
			write!(f, ", stderr: {}", self.stderr.as_str().unwrap_or(""))
		} else {
			write!(f, ", stdout: {}", self.stdout.as_str().unwrap_or(""))
		}
	}
}