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}