Skip to main content

dataflow_rs/observe/
status.rs

1//! How a run and each workflow in it ended.
2
3use crate::base::{Error, WorkflowIx};
4use serde::ser::SerializeStruct;
5use serde::{Serialize, Serializer};
6
7/// How a run ended.
8#[derive(Debug)]
9#[non_exhaustive]
10pub enum RunStatus {
11    /// Every considered workflow finished and the message holds no errors.
12    Completed,
13    /// Every considered workflow finished, but the message holds error records.
14    CompletedWithErrors,
15    /// A workflow stopped the run with this error. A function's `Err` of
16    /// kind `TimedOut` or `Cancelled`, such as a child run's propagated,
17    /// ends the run here when its task's policies stop the run and it
18    /// returned before the run's own deadline passed or its token was
19    /// cancelled: only the run's own limits give the two statuses below.
20    Stopped(Error),
21    /// The run's deadline passed: at a check between ops or at a sweep, or
22    /// while a call ran that it then cut short or that returned `Err`
23    /// after it.
24    TimedOut,
25    /// The run's token, or one of its ancestors, was cancelled: before a
26    /// check, or while a call ran that it then cut short or that returned
27    /// `Err` after it.
28    Cancelled,
29}
30
31/// Why a workflow did or did not run.
32#[derive(Debug)]
33#[non_exhaustive]
34pub enum Selection {
35    /// The workflow was selected and ran.
36    Ran,
37    /// Its condition was false.
38    SkippedByCondition,
39    /// Its condition failed to evaluate, which counts as false.
40    ConditionFailed(Error),
41    /// The message's routing bucket is outside its rollout.
42    SkippedByRollout,
43}
44
45/// How a workflow that ran ended.
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47#[non_exhaustive]
48pub enum WorkflowExit {
49    /// It ran to its end.
50    Completed,
51    /// A task or a halt ended it early, on purpose.
52    Halted,
53    /// A task failed and `on_failure: continue` stepped past it.
54    Failed,
55    /// It stopped the run.
56    Stopped,
57}
58
59/// One workflow's part in a run.
60#[derive(Debug)]
61#[non_exhaustive]
62pub struct WorkflowRecord<'r> {
63    /// The workflow's id.
64    pub id: &'r str,
65    /// The workflow's position in the program.
66    pub index: WorkflowIx,
67    /// Whether it was selected.
68    pub selection: &'r Selection,
69    /// The sweeps begun: one for a workflow without a loop that ran, and for
70    /// a loop each sweep its condition admitted.
71    pub sweeps: u32,
72    /// How it ended, if it ran.
73    pub exit: Option<WorkflowExit>,
74}
75
76/// Writes the `status` string of a run's JSON: `completed`,
77/// `completed_with_errors`, `stopped`, `timed_out` or `cancelled`. The
78/// `error` of a stopped run is a key of the run's object, which
79/// [`Run`](crate::Run) writes.
80impl Serialize for RunStatus {
81    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
82        s.serialize_str(match self {
83            Self::Completed => "completed",
84            Self::CompletedWithErrors => "completed_with_errors",
85            Self::Stopped(_) => "stopped",
86            Self::TimedOut => "timed_out",
87            Self::Cancelled => "cancelled",
88        })
89    }
90}
91
92/// Writes the `selection` string of a workflow's record: `ran`,
93/// `skipped_by_condition`, `condition_failed` or `skipped_by_rollout`. The
94/// error of a failed condition is a key of the record, which
95/// [`WorkflowRecord`] writes.
96impl Serialize for Selection {
97    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
98        s.serialize_str(match self {
99            Self::Ran => "ran",
100            Self::SkippedByCondition => "skipped_by_condition",
101            Self::ConditionFailed(_) => "condition_failed",
102            Self::SkippedByRollout => "skipped_by_rollout",
103        })
104    }
105}
106
107/// Writes how a workflow ended as a lowercase string: `completed`,
108/// `halted`, `failed` or `stopped`.
109impl Serialize for WorkflowExit {
110    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
111        s.serialize_str(match self {
112            Self::Completed => "completed",
113            Self::Halted => "halted",
114            Self::Failed => "failed",
115            Self::Stopped => "stopped",
116        })
117    }
118}
119
120/// Writes a workflow's record as `{"id", "index", "selection", "error"?,
121/// "sweeps", "exit"}`: `error` only for a condition that failed to
122/// evaluate, and `exit` `null` for a workflow that did not run.
123impl Serialize for WorkflowRecord<'_> {
124    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
125        let error = match self.selection {
126            Selection::ConditionFailed(e) => Some(e),
127            _ => None,
128        };
129        let mut o = s.serialize_struct("WorkflowRecord", 6)?;
130        o.serialize_field("id", self.id)?;
131        o.serialize_field("index", &self.index)?;
132        o.serialize_field("selection", self.selection)?;
133        match error {
134            Some(e) => o.serialize_field("error", e)?,
135            None => o.skip_field("error")?,
136        }
137        o.serialize_field("sweeps", &self.sweeps)?;
138        o.serialize_field("exit", &self.exit)?;
139        o.end()
140    }
141}
142
143#[cfg(feature = "ts")]
144crate::base::ts::ts_decl!(
145    RunStatus,
146    "RunStatus",
147    [" How a run ended: a stopped run's error is its `error`."],
148    |_cfg| crate::base::ts::literals([
149        "completed",
150        "completed_with_errors",
151        "stopped",
152        "timed_out",
153        "cancelled",
154    ])
155);
156
157#[cfg(feature = "ts")]
158crate::base::ts::ts_decl!(
159    Selection,
160    "Selection",
161    [
162        " Why a workflow did or did not run: a failed condition's error is its",
163        " record's `error`.",
164    ],
165    |_cfg| crate::base::ts::literals([
166        "ran",
167        "skipped_by_condition",
168        "condition_failed",
169        "skipped_by_rollout",
170    ])
171);
172
173#[cfg(feature = "ts")]
174crate::base::ts::ts_decl!(
175    WorkflowExit,
176    "WorkflowExit",
177    [" How a workflow that ran ended."],
178    |_cfg| crate::base::ts::literals(["completed", "halted", "failed", "stopped"])
179);
180
181#[cfg(feature = "ts")]
182crate::base::ts::ts_decl!(
183    WorkflowRecord<'_>,
184    "WorkflowRecord",
185    [" One workflow's part in a run."],
186    |cfg| {
187        use crate::base::ts::{Prop, object};
188        object(&[
189            Prop::new("id", "string", &[" The workflow's id."]),
190            Prop::new(
191                "index",
192                WorkflowIx::name(cfg),
193                &[" The workflow's position in the program."],
194            ),
195            Prop::new(
196                "selection",
197                Selection::name(cfg),
198                &[" Whether it was selected."],
199            ),
200            Prop::optional(
201                "error",
202                Error::name(cfg),
203                &[
204                    " Why its condition failed to evaluate; present only when `selection` is",
205                    " `condition_failed`.",
206                ],
207            ),
208            Prop::new(
209                "sweeps",
210                "number",
211                &[
212                    " The sweeps begun: one for a workflow without a loop that ran, and for",
213                    " a loop each sweep its condition admitted.",
214                ],
215            ),
216            Prop::new(
217                "exit",
218                Option::<WorkflowExit>::name(cfg),
219                &[" How it ended; `null` when it did not run."],
220            ),
221        ])
222    }
223);