1use std::fmt;
4use std::str::FromStr;
5
6use crate::explore::measure::SeriesBuffer;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct PlannedRun {
11 pub run_id: u64,
17 pub config_id: u64,
19 pub rep: u64,
21 pub seed: u64,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum RunStatus {
28 Ok,
30 NonFinite,
32 Panicked,
34 GpuError,
36 Refused,
38 ShapeError,
40 TimedOut,
42}
43
44impl RunStatus {
45 pub fn as_str(self) -> &'static str {
47 match self {
48 Self::Ok => "ok",
49 Self::NonFinite => "non_finite",
50 Self::Panicked => "panicked",
51 Self::GpuError => "gpu_error",
52 Self::Refused => "refused",
53 Self::ShapeError => "shape_error",
54 Self::TimedOut => "timed_out",
55 }
56 }
57
58 pub fn is_failure(self) -> bool {
60 match self {
61 Self::Ok | Self::NonFinite => false,
62 Self::Panicked | Self::GpuError | Self::Refused | Self::ShapeError | Self::TimedOut => true,
63 }
64 }
65}
66
67impl FromStr for RunStatus {
68 type Err = RunStatusError;
69
70 fn from_str(raw: &str) -> Result<Self, Self::Err> {
72 match raw {
73 "ok" => Ok(Self::Ok),
74 "non_finite" => Ok(Self::NonFinite),
75 "panicked" => Ok(Self::Panicked),
76 "gpu_error" => Ok(Self::GpuError),
77 "refused" => Ok(Self::Refused),
78 "shape_error" => Ok(Self::ShapeError),
79 "timed_out" => Ok(Self::TimedOut),
80 _ => Err(RunStatusError { raw: raw.to_owned() }),
81 }
82 }
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
87pub struct RunStatusError {
88 pub raw: String,
90}
91
92impl fmt::Display for RunStatusError {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 write!(f, "unknown run status '{}'", self.raw)
95 }
96}
97
98impl std::error::Error for RunStatusError {}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102pub enum StopReason {
103 Steps,
105 Condition,
107 Fault,
109 Timeout,
111}
112
113impl StopReason {
114 pub fn as_str(self) -> &'static str {
116 match self {
117 Self::Steps => "steps",
118 Self::Condition => "condition",
119 Self::Fault => "fault",
120 Self::Timeout => "timeout",
121 }
122 }
123}
124
125impl FromStr for StopReason {
126 type Err = StopReasonError;
127
128 fn from_str(raw: &str) -> Result<Self, Self::Err> {
130 match raw {
131 "steps" => Ok(Self::Steps),
132 "condition" => Ok(Self::Condition),
133 "fault" => Ok(Self::Fault),
134 "timeout" => Ok(Self::Timeout),
135 _ => Err(StopReasonError { raw: raw.to_owned() }),
136 }
137 }
138}
139
140#[derive(Debug, Clone, PartialEq, Eq)]
142pub struct StopReasonError {
143 pub raw: String,
145}
146
147impl fmt::Display for StopReasonError {
148 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149 write!(f, "unknown stop reason '{}'", self.raw)
150 }
151}
152
153impl std::error::Error for StopReasonError {}
154
155#[derive(Debug, Clone, PartialEq)]
157pub struct RunOutcome {
158 pub run: PlannedRun,
160 pub run_key: u64,
162 pub status: RunStatus,
164 pub stop_reason: StopReason,
166 pub ticks: u64,
171 pub population: u64,
173 pub build_ms: f64,
175 pub wall_ms: f64,
177 pub reducers: Vec<Option<f64>>,
179 pub series: SeriesBuffer,
181 pub note: Option<String>,
183}
184
185impl RunOutcome {
186 pub fn steps_per_s(&self) -> f64 {
188 self.ticks as f64 * 1000.0 / self.wall_ms
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::{RunStatus, StopReason};
195
196 #[test]
197 fn statuses_are_written_in_snake_case() {
198 let statuses = [
199 RunStatus::Ok,
200 RunStatus::NonFinite,
201 RunStatus::Panicked,
202 RunStatus::GpuError,
203 RunStatus::Refused,
204 RunStatus::ShapeError,
205 RunStatus::TimedOut,
206 ];
207 let names: Vec<&str> = statuses.iter().map(|status| status.as_str()).collect();
208 assert_eq!(
209 names,
210 [
211 "ok",
212 "non_finite",
213 "panicked",
214 "gpu_error",
215 "refused",
216 "shape_error",
217 "timed_out"
218 ]
219 );
220 let failures: Vec<bool> = statuses.iter().map(|status| status.is_failure()).collect();
221 assert_eq!(failures, [false, false, true, true, true, true, true]);
222 for status in statuses {
223 assert_eq!(status.as_str().parse(), Ok(status));
224 }
225 assert!("timed out".parse::<RunStatus>().is_err());
226 let reasons = [
227 StopReason::Steps,
228 StopReason::Condition,
229 StopReason::Fault,
230 StopReason::Timeout,
231 ];
232 let names: Vec<&str> = reasons.iter().map(|reason| reason.as_str()).collect();
233 assert_eq!(names, ["steps", "condition", "fault", "timeout"]);
234 for reason in reasons {
235 assert_eq!(reason.as_str().parse(), Ok(reason));
236 }
237 assert!("stopped".parse::<StopReason>().is_err());
238 }
239}