1use core::fmt;
2use std::{borrow::Cow, collections::BTreeMap, sync::Arc};
3
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7use crate::Rack;
8
9#[derive(Deserialize, Serialize, Debug, Clone, Copy, Eq, PartialEq)]
11#[serde(rename_all = "lowercase")]
12pub enum InputKind {
13 Flow,
15 External,
17}
18
19#[derive(Serialize, Deserialize, Debug, Clone)]
21pub struct LineState {
22 name: Cow<'static, str>,
23 steps: Vec<StepState>,
24}
25
26impl fmt::Display for LineState {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 write!(f, "{}: ", self.name)?;
29 let mut passed = true;
30 for (step_no, step) in self.steps.iter().enumerate() {
31 if step_no > 0 {
32 write!(f, " -> ")?;
33 }
34 match step {
35 StepState::Single(s) => {
36 write!(f, "{}", s.name())?;
37 if s.input() != &Value::Null {
38 write!(f, "({})", s.input())?;
39 }
40 }
41 StepState::Multi(ss) => {
42 write!(f, "( ")?;
43 for (s_no, s) in ss.iter().enumerate() {
44 if s_no > 0 {
45 write!(f, " | ")?;
46 }
47 write!(f, "{}", s.name())?;
48 if s.input() != &Value::Null {
49 write!(f, "(")?;
50 match s.input_kind() {
51 InputKind::Flow => {}
52 InputKind::External => {
53 write!(f, "\\->")?;
54 }
55 }
56 write!(f, "{})", s.input())?;
57 }
58 }
59 write!(f, " )")?;
60 }
61 }
62 if passed && !step.passed() {
63 passed = false;
64 write!(f, " !")?;
65 }
66 }
67 Ok(())
68 }
69}
70
71impl LineState {
72 pub(crate) fn new(name: impl Into<Cow<'static, str>>) -> Self {
73 LineState {
74 name: name.into(),
75 steps: Vec::new(),
76 }
77 }
78 pub fn name(&self) -> &str {
80 self.name.as_ref()
81 }
82 pub fn steps(&self) -> &[StepState] {
84 &self.steps
85 }
86 pub fn steps_mut(&mut self) -> &mut [StepState] {
88 &mut self.steps
89 }
90 pub(crate) fn extend<I>(&mut self, step_states: I)
100 where
101 I: IntoIterator<Item = StepStateInfo>,
102 {
103 let steps = step_states.into_iter().collect();
104 self.steps.push(StepState::Multi(steps));
105 }
106 pub(crate) fn push_step_state(
107 &mut self,
108 name: impl Into<Cow<'static, str>>,
109 input: Value,
110 input_kind: InputKind,
111 passed: bool,
112 ) {
113 self.steps
114 .push(StepState::Single(StepStateInfo::new_with_serialized_input(
115 name, input, input_kind, passed,
116 )));
117 }
118 pub(crate) fn clear(&mut self) {
119 self.steps.clear();
120 }
121}
122
123#[derive(Serialize, Deserialize, Clone, Debug)]
125#[serde(untagged)]
126pub enum StepState {
127 Single(StepStateInfo),
129 Multi(Vec<StepStateInfo>),
131}
132
133impl StepState {
134 pub fn passed(&self) -> bool {
136 match self {
137 StepState::Single(single) => single.passed(),
138 #[allow(clippy::redundant_closure_for_method_calls)]
139 StepState::Multi(multi) => multi.iter().all(|s| s.passed()),
140 }
141 }
142 pub fn info(&self) -> Vec<&StepStateInfo> {
144 match self {
145 StepState::Single(single) => vec![single],
146 StepState::Multi(multi) => multi.iter().collect::<Vec<_>>(),
147 }
148 }
149 pub fn info_mut(&mut self) -> Vec<&mut StepStateInfo> {
151 match self {
152 StepState::Single(single) => vec![single],
153 StepState::Multi(multi) => multi.iter_mut().collect::<Vec<_>>(),
154 }
155 }
156}
157
158#[derive(Serialize, Deserialize)]
160pub struct StepStateInfo {
161 #[serde(flatten)]
162 inner: Arc<StepStateInner>,
163}
164
165impl Clone for StepStateInfo {
166 fn clone(&self) -> Self {
167 StepStateInfo {
168 inner: Arc::clone(&self.inner),
169 }
170 }
171}
172
173impl fmt::Debug for StepStateInfo {
174 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
175 f.debug_struct("StepState")
176 .field("name", &self.inner.name)
177 .field("input", &self.inner.input)
178 .field("passed", &self.inner.passed)
179 .finish()
180 }
181}
182
183#[derive(Serialize, Deserialize)]
184struct StepStateInner {
185 name: Cow<'static, str>,
186 input: Value,
187 input_kind: InputKind,
188 passed: bool,
189}
190
191impl StepStateInfo {
192 pub fn to_modified(
194 &self,
195 name: Option<&str>,
196 input: Option<Value>,
197 input_kind: Option<InputKind>,
198 passed: Option<bool>,
199 ) -> Self {
200 StepStateInfo {
201 inner: Arc::new(StepStateInner {
202 name: name.map_or_else(|| self.inner.name.clone(), |n| n.to_owned().into()),
203 input: input.unwrap_or_else(|| self.inner.input.clone()),
204 input_kind: input_kind.unwrap_or(self.inner.input_kind),
205 passed: passed.unwrap_or(self.inner.passed),
206 }),
207 }
208 }
209 pub(crate) fn new<INPUT: Serialize>(
210 name: impl Into<Cow<'static, str>>,
211 input: INPUT,
212 input_kind: InputKind,
213 passed: bool,
214 ) -> Self {
215 StepStateInfo {
216 inner: Arc::new(StepStateInner {
217 name: name.into(),
218 input: serde_json::to_value(input).unwrap_or_default(),
219 input_kind,
220 passed,
221 }),
222 }
223 }
224 pub(crate) fn new_with_serialized_input(
225 name: impl Into<Cow<'static, str>>,
226 input: Value,
227 input_kind: InputKind,
228 passed: bool,
229 ) -> Self {
230 StepStateInfo {
231 inner: Arc::new(StepStateInner {
232 name: name.into(),
233 input,
234 input_kind,
235 passed,
236 }),
237 }
238 }
239 pub fn name(&self) -> &str {
241 self.inner.name.as_ref()
242 }
243 pub fn input(&self) -> &Value {
245 &self.inner.input
246 }
247 pub fn input_kind(&self) -> InputKind {
249 self.inner.input_kind
250 }
251 pub fn passed(&self) -> bool {
253 self.inner.passed
254 }
255}
256
257impl fmt::Display for Rack {
258 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259 for (i, line) in self.lines.values().enumerate() {
260 if i > 0 {
261 writeln!(f)?;
262 }
263 write!(f, "{}", line)?;
264 }
265 Ok(())
266 }
267}
268
269pub trait SnapshotFormatter: Send + Sync {
271 fn format(&self, snapshot: Snapshot) -> Snapshot;
273}
274
275#[derive(Default, Debug, Clone, Serialize, Deserialize)]
277pub struct Snapshot {
278 pub(crate) lines: BTreeMap<Cow<'static, str>, LineState>,
279}
280
281impl Snapshot {
282 pub fn line_state(&self, name: &str) -> Option<&LineState> {
284 self.lines.get(name)
285 }
286 pub fn lines(&self) -> &BTreeMap<Cow<'static, str>, LineState> {
288 &self.lines
289 }
290 pub fn line_state_mut(&mut self, name: &str) -> Option<&mut LineState> {
292 self.lines.get_mut(name)
293 }
294 pub fn lines_mut(&mut self) -> &mut BTreeMap<Cow<'static, str>, LineState> {
296 &mut self.lines
297 }
298}
299
300impl fmt::Display for Snapshot {
301 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302 for (i, line) in self.lines.values().enumerate() {
303 if i > 0 {
304 writeln!(f)?;
305 }
306 write!(f, "{}", line)?;
307 }
308 Ok(())
309 }
310}