crankshaft_engine/task/
execution.rs1use std::collections::BTreeMap;
4use std::fmt::Display;
5use std::process::ExitStatus;
6
7use bon::Builder;
8use indexmap::IndexMap;
9use nonempty::NonEmpty;
10
11#[derive(Debug)]
13pub struct NoImageError;
14
15impl Display for NoImageError {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 write!(f, "no image specified")
18 }
19}
20
21impl std::error::Error for NoImageError {}
22
23#[derive(Builder, Clone, Debug)]
25#[builder(builder_type = Builder)]
26pub struct Execution {
27 #[builder(with = |iter: impl IntoIterator<Item = impl Into<String>>| -> Result<_, NoImageError> {
34 NonEmpty::collect(iter.into_iter().map(Into::into)).ok_or(NoImageError)
35 })]
36 pub(crate) images: NonEmpty<String>,
37
38 #[builder(into)]
40 pub(crate) program: String,
41
42 #[builder(into, default)]
44 pub(crate) args: Vec<String>,
45
46 #[builder(into)]
48 pub(crate) work_dir: Option<String>,
49
50 #[builder(into)]
53 pub(crate) stdin: Option<String>,
54
55 #[builder(into)]
58 pub(crate) stdout: Option<String>,
59
60 #[builder(into)]
63 pub(crate) stderr: Option<String>,
64
65 #[builder(into, default)]
67 pub(crate) env: IndexMap<String, String>,
68}
69
70impl Execution {
71 pub fn images(&self) -> &NonEmpty<String> {
73 &self.images
74 }
75
76 pub fn program(&self) -> &str {
78 &self.program
79 }
80
81 pub fn args(&self) -> &[String] {
83 &self.args
84 }
85
86 pub fn work_dir(&self) -> Option<&str> {
88 self.work_dir.as_deref()
89 }
90
91 pub fn stdin(&self) -> Option<&str> {
93 self.stdin.as_deref()
94 }
95
96 pub fn stdout(&self) -> Option<&str> {
98 self.stdout.as_deref()
99 }
100
101 pub fn stderr(&self) -> Option<&str> {
103 self.stderr.as_deref()
104 }
105
106 pub fn env(&self) -> &IndexMap<String, String> {
108 &self.env
109 }
110}
111
112impl From<Execution> for tes::v1::types::task::Executor {
113 fn from(execution: Execution) -> Self {
114 let env = execution
115 .env
116 .into_iter()
117 .collect::<BTreeMap<String, String>>();
118
119 let env = if env.is_empty() { None } else { Some(env) };
120
121 let mut command = Vec::with_capacity(execution.args.len() + 1);
122 command.push(execution.program);
123 command.extend(execution.args);
124
125 tes::v1::types::task::Executor {
126 image: execution.images.first().into(),
127 command,
128 workdir: execution.work_dir,
129 stdin: execution.stdin,
130 stdout: execution.stdout,
131 stderr: execution.stderr,
132 env,
133 ignore_error: Some(true),
134 }
135 }
136}
137
138#[derive(Clone, Debug)]
140pub struct ExecutionResult {
141 pub image: Option<String>,
146 pub status: ExitStatus,
148}