asimov_runner/
executor_error.rs1use crate::SysexitsError;
10use alloc::{string::String, vec::Vec};
11use core::fmt;
12use std::{ffi::OsString, io::Cursor};
13
14pub type ExecutorResult = std::result::Result<Cursor<Vec<u8>>, ExecutorError>;
20
21#[derive(Debug)]
27pub enum ExecutorError {
28 UnsupportedOption(&'static str),
31 MissingProgram(OsString),
33 SpawnFailure(std::io::Error),
35 Failure(SysexitsError, Option<String>),
37 UnexpectedFailure(Option<i32>, Option<String>),
42 UnexpectedOther(std::io::Error),
45 IncompleteInput,
48}
49
50impl core::error::Error for ExecutorError {}
51
52impl From<crate::JsonlLineError> for ExecutorError {
53 fn from(error: crate::JsonlLineError) -> Self {
54 std::io::Error::new(std::io::ErrorKind::InvalidData, error).into()
55 }
56}
57
58impl fmt::Display for ExecutorError {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 match self {
61 Self::UnsupportedOption(option) => {
62 write!(f, "Program does not support requested option: {}", option)
63 },
64 Self::MissingProgram(program) => {
65 write!(f, "Missing program: {}", program.to_string_lossy())
66 },
67 Self::SpawnFailure(err) => write!(f, "Failed to spawn process: {}", err),
68 Self::Failure(error, stderr) => {
69 write!(
70 f,
71 "Command failed with exit code {}",
72 error.code().unwrap_or(-1),
73 )?;
74 if let Some(stderr) = stderr {
75 write!(f, "\n{}", stderr)?;
76 }
77 Ok(())
78 },
79 Self::UnexpectedFailure(code, stderr) => {
80 write!(
81 f,
82 "Command failed with unexpected exit code: {}",
83 code.unwrap_or(-1)
84 )?;
85 if let Some(stderr) = stderr {
86 write!(f, "\n{}", stderr)?;
87 }
88 Ok(())
89 },
90 Self::UnexpectedOther(err) => write!(f, "Unexpected error: {}", err),
91 Self::IncompleteInput => write!(f, "Process exited before input delivery completed"),
92 }
93 }
94}
95
96#[cfg(feature = "std")]
97impl From<std::io::Error> for ExecutorError {
98 fn from(error: std::io::Error) -> Self {
99 Self::UnexpectedOther(error)
100 }
101}
102
103#[cfg(feature = "std")]
104impl From<std::process::ExitStatus> for ExecutorError {
105 fn from(status: std::process::ExitStatus) -> Self {
106 match SysexitsError::try_from(status) {
107 Ok(error) => Self::Failure(error, None),
108 Err(code) => Self::UnexpectedFailure(code, None),
109 }
110 }
111}
112
113#[cfg(feature = "std")]
114impl From<std::process::Output> for ExecutorError {
115 fn from(output: std::process::Output) -> Self {
116 let stderr = String::from_utf8(output.stderr).ok();
117 match SysexitsError::try_from(output.status) {
118 Ok(error) => Self::Failure(error, stderr),
119 Err(code) => Self::UnexpectedFailure(code, stderr),
120 }
121 }
122}