Skip to main content

asimov_runner/
executor_error.rs

1// This is free and unencumbered software released into the public domain.
2
3use crate::SysexitsError;
4use core::fmt;
5use std::{ffi::OsString, io::Cursor};
6
7pub type ExecutorResult = std::result::Result<Cursor<Vec<u8>>, ExecutorError>;
8
9#[derive(Debug)]
10pub enum ExecutorError {
11    MissingProgram(OsString),
12    SpawnFailure(std::io::Error),
13    Failure(SysexitsError, Option<String>),
14    UnexpectedFailure(Option<i32>, Option<String>),
15    UnexpectedOther(std::io::Error),
16}
17
18impl core::error::Error for ExecutorError {}
19
20impl fmt::Display for ExecutorError {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        match self {
23            Self::MissingProgram(program) => {
24                write!(f, "Missing program: {}", program.to_string_lossy())
25            },
26            Self::SpawnFailure(err) => write!(f, "Failed to spawn process: {}", err),
27            Self::Failure(error, stderr) => {
28                write!(
29                    f,
30                    "Command failed with exit code {}",
31                    error.code().unwrap_or(-1),
32                )?;
33                if let Some(stderr) = stderr {
34                    write!(f, "\n{}", stderr)?;
35                }
36                Ok(())
37            },
38            Self::UnexpectedFailure(code, stderr) => {
39                write!(
40                    f,
41                    "Command failed with unexpected exit code: {}",
42                    code.unwrap_or(-1)
43                )?;
44                if let Some(stderr) = stderr {
45                    write!(f, "\n{}", stderr)?;
46                }
47                Ok(())
48            },
49            Self::UnexpectedOther(err) => write!(f, "Unexpected error: {}", err),
50        }
51    }
52}
53
54#[cfg(feature = "std")]
55impl From<std::io::Error> for ExecutorError {
56    fn from(error: std::io::Error) -> Self {
57        Self::UnexpectedOther(error)
58    }
59}
60
61#[cfg(feature = "std")]
62impl From<std::process::ExitStatus> for ExecutorError {
63    fn from(status: std::process::ExitStatus) -> Self {
64        match SysexitsError::try_from(status) {
65            Ok(error) => Self::Failure(error, None),
66            Err(code) => Self::UnexpectedFailure(code, None),
67        }
68    }
69}
70
71#[cfg(feature = "std")]
72impl From<std::process::Output> for ExecutorError {
73    fn from(output: std::process::Output) -> Self {
74        let stderr = String::from_utf8(output.stderr).ok();
75        match SysexitsError::try_from(output.status) {
76            Ok(error) => Self::Failure(error, stderr),
77            Err(code) => Self::UnexpectedFailure(code, stderr),
78        }
79    }
80}