use std::{
fmt::{self, Debug},
process::{ExitCode, Termination},
};
use thiserror::Error;
use thiserror_ext::AsReport;
use tokio::{io, task::JoinError};
use crate::{
exec::{Output, StatusCode},
print,
};
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum Error {
#[allow(missing_docs)]
#[error("failed to parse arguments: {msg}")]
ArgParseError { msg: String },
#[error("failed to parse config")]
ConfigError(#[from] Box<figment::Error>),
#[error("failed to get exit code of subprocess")]
CmdJoinError(#[from] JoinError),
#[error("failed to spawn subprocess")]
CmdSpawnError(#[source] io::Error),
#[allow(missing_docs)]
#[error("subprocess didn't have a handle to {handle}")]
CmdNoHandleError { handle: String },
#[error("subprocess failed while running")]
CmdWaitError(#[source] io::Error),
#[allow(missing_docs)]
#[error("subprocess exited with code {code}")]
CmdStatusCodeError { code: StatusCode, output: Output },
#[error("subprocess interrupted by signal")]
CmdInterruptedError,
#[error(transparent)]
FromUtf8Error(#[from] std::string::FromUtf8Error),
#[error(transparent)]
DialogError(#[from] dialoguer::Error),
#[error(transparent)]
IoError(#[from] io::Error),
#[allow(missing_docs)]
#[error("operation `{op}` is unimplemented for `{pm}`")]
OperationUnimplementedError { op: String, pm: String },
#[error("{0}")]
OtherError(String),
}
#[allow(clippy::module_name_repetitions)]
pub struct MainError(Error);
impl From<Error> for MainError {
fn from(e: Error) -> Self {
Self(e)
}
}
impl Debug for MainError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\r")?;
print::write_err(f, &*print::prompt::ERROR, self.0.as_report())
}
}
impl Termination for MainError {
fn report(self) -> ExitCode {
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
match self.0 {
Error::CmdStatusCodeError { code, .. } => code as u8,
_ => 1,
}
.into()
}
}