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(""))
		}
	}
}