Skip to main content

proef_core/
step.rs

1//! Lowered, engine-agnostic step and batch types (TECH-SPEC §3).
2//!
3//! A scenario lowers to an ordered list of [`LoweredStep`]s, segmented into
4//! [`StepBatch`]es of **contiguous same-engine steps** (batched maximally — splits
5//! happen only at `optional:` boundaries and engine changes, ADR-0010). Engines
6//! return a [`BatchResult`] with one [`StepOutcome`] per executed step.
7
8use std::sync::Arc;
9use std::time::Duration;
10
11use serde::{Deserialize, Serialize};
12
13use crate::engine::EngineId;
14use crate::error::EngineError;
15
16/// Anchor back to the authored `.feature` source (file, 1-based line, step text).
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18pub struct StepRef {
19    /// Feature file path as authored.
20    pub file: Arc<str>,
21    /// 1-based line of the step keyword in the feature file.
22    pub line: usize,
23    /// The full step text (keyword stripped).
24    pub text: Arc<str>,
25}
26
27/// Identifies the *kind* of a macro step (`hurl`, …). A step kind
28/// names the engine that claims it via [`crate::engine::StepKindSpec`] (ADR-0002).
29#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
30pub struct StepKindId(Arc<str>);
31
32impl StepKindId {
33    /// The kind name as written in packs (without the trailing `:`).
34    pub fn as_str(&self) -> &str {
35        &self.0
36    }
37}
38
39impl From<&str> for StepKindId {
40    fn from(s: &str) -> Self {
41        Self(Arc::from(s))
42    }
43}
44
45impl std::fmt::Display for StepKindId {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        f.write_str(&self.0)
48    }
49}
50
51/// The engine-facing payload of a lowered step.
52#[derive(Debug, Clone, PartialEq)]
53pub enum StepPayload {
54    /// Lowered hurl text (`${…}` resolved, `{{…}}` untouched) — one or more entries.
55    HurlEntries(String),
56    /// Structured payload — reserved for future non-hurl engines (ADR-0004).
57    Structured(serde_json::Value),
58}
59
60/// Finite retry policy for a step (`retry:` — infinite retries are rejected at pack
61/// load by the finite-retry lint, ADR-0007).
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
63pub struct Retry {
64    /// Maximum number of retries (finite by construction).
65    pub count: u32,
66    /// Interval between attempts, in milliseconds.
67    pub interval_ms: u64,
68}
69
70/// A `when:` skip guard: the step runs iff the expression is non-empty after
71/// `${…}` resolution (TECH-SPEC §6).
72#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
73pub struct Guard(pub String);
74
75/// One step after macro expansion and `${…}` lowering — engine-agnostic.
76#[derive(Debug, Clone, PartialEq)]
77pub struct LoweredStep {
78    /// Anchor to the authored feature line.
79    pub step: StepRef,
80    /// Which step kind (and therefore which engine) executes this step.
81    pub kind: StepKindId,
82    /// The engine-facing payload.
83    pub payload: StepPayload,
84    /// `optional:` steps warn instead of failing (and segment the batch).
85    pub optional: bool,
86    /// Finite retry policy, when configured.
87    pub retry: Option<Retry>,
88    /// Skip guard, when configured (resolved text; the runtime skips the step
89    /// when it is empty).
90    pub when: Option<Guard>,
91    /// Pack-step entry label (events/console), when authored.
92    pub label: Option<String>,
93    /// `saveAs:` promotions: capture name → `global` (ADR-0005).
94    pub save_as: std::collections::BTreeMap<String, String>,
95}
96
97/// A contiguous run of same-engine steps, dispatched as one unit (ADR-0002).
98#[derive(Debug, Clone, PartialEq)]
99pub struct StepBatch {
100    /// Ordinal of this batch within the *scenario* (the sidecar map's `batch`
101    /// key). Engines must select sidecar entries by this index — a per-session
102    /// counter diverges as soon as another engine's batch interleaves.
103    pub index: usize,
104    /// The engine that executes this batch.
105    pub engine: EngineId,
106    /// The steps, in authored order.
107    pub steps: Vec<LoweredStep>,
108}
109
110/// Outcome status of a step or scenario.
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
112#[serde(rename_all = "snake_case")]
113pub enum Status {
114    /// Ran and passed.
115    Passed,
116    /// Ran and failed.
117    Failed,
118    /// Not run (guard, earlier failure, filter).
119    Skipped,
120    /// An `optional:` step failed — reported as a warning, run continues.
121    Warned,
122}
123
124/// Per-step result reported by an engine.
125#[derive(Debug, Clone)]
126pub struct StepOutcome {
127    /// Anchor to the authored feature line.
128    pub step: StepRef,
129    /// Outcome status.
130    pub status: Status,
131    /// Number of attempts made (≥ 1 once the step ran).
132    pub attempts: u32,
133    /// Wall-clock duration of all attempts.
134    pub duration: Duration,
135    /// Engine-specific detail (assert message, timing breakdown, …).
136    pub detail: Option<String>,
137    /// Line span in the emitted artifact, when one exists (ADR-0010 sidecar).
138    pub artifact_span: Option<(u32, u32)>,
139}
140
141/// Result of dispatching one [`StepBatch`] to an engine.
142#[derive(Debug)]
143pub struct BatchResult {
144    /// One outcome per step the engine reached.
145    pub steps: Vec<StepOutcome>,
146    /// A batch-level failure, if the batch stopped early.
147    pub error: Option<EngineError>,
148}