Skip to main content

logicline/
recording.rs

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/// Input kind, flow: taken from the previous action, external: specified by the user
10#[derive(Deserialize, Serialize, Debug, Clone, Copy, Eq, PartialEq)]
11#[serde(rename_all = "lowercase")]
12pub enum InputKind {
13    /// Taken from the previous action
14    Flow,
15    /// Specified by the user
16    External,
17}
18
19/// State of the logical line
20#[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    /// Name of the line
79    pub fn name(&self) -> &str {
80        self.name.as_ref()
81    }
82    /// Steps states of the line
83    pub fn steps(&self) -> &[StepState] {
84        &self.steps
85    }
86    /// Steps states of the line, mutable
87    pub fn steps_mut(&mut self) -> &mut [StepState] {
88        &mut self.steps
89    }
90    //pub(crate) fn push_step_state<INPUT: Serialize>(
91    //&mut self,
92    //name: impl Into<Cow<'static, str>>,
93    //input: INPUT,
94    //passed: bool,
95    //) {
96    //self.steps
97    //.push(StepState::Single(StepStateSingle::new(name, input, passed)));
98    //}
99    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/// Line step state
124#[derive(Serialize, Deserialize, Clone, Debug)]
125#[serde(untagged)]
126pub enum StepState {
127    /// Single step state (single flow)
128    Single(StepStateInfo),
129    /// Multiple step state (logical OR)
130    Multi(Vec<StepStateInfo>),
131}
132
133impl StepState {
134    /// Has the step been passed
135    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    /// Step state info, single-value vector for single step state, multi-value vector for multi step state
143    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    /// Step state info mutable
150    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/// Single step state information
159#[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    /// Returns modified version of the step state info
193    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    /// Step name
240    pub fn name(&self) -> &str {
241        self.inner.name.as_ref()
242    }
243    /// Step input (serialized as [`serde_json::Value`])
244    pub fn input(&self) -> &Value {
245        &self.inner.input
246    }
247    /// Step input kind (flow or external)
248    pub fn input_kind(&self) -> InputKind {
249        self.inner.input_kind
250    }
251    /// Has the step been passed
252    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
269/// Modifies snapshots before serving/displaying
270pub trait SnapshotFormatter: Send + Sync {
271    /// Format the snapshot
272    fn format(&self, snapshot: Snapshot) -> Snapshot;
273}
274
275/// State snapshot
276#[derive(Default, Debug, Clone, Serialize, Deserialize)]
277pub struct Snapshot {
278    pub(crate) lines: BTreeMap<Cow<'static, str>, LineState>,
279}
280
281impl Snapshot {
282    /// State of the line
283    pub fn line_state(&self, name: &str) -> Option<&LineState> {
284        self.lines.get(name)
285    }
286    /// Lines map
287    pub fn lines(&self) -> &BTreeMap<Cow<'static, str>, LineState> {
288        &self.lines
289    }
290    /// Mutable state of the line
291    pub fn line_state_mut(&mut self, name: &str) -> Option<&mut LineState> {
292        self.lines.get_mut(name)
293    }
294    /// Lines map
295    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}