use std::sync::Arc;
use std::time::Duration;
use serde::{Deserialize, Serialize};
use crate::engine::EngineId;
use crate::error::EngineError;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StepRef {
pub file: Arc<str>,
pub line: usize,
pub text: Arc<str>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct StepKindId(Arc<str>);
impl StepKindId {
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for StepKindId {
fn from(s: &str) -> Self {
Self(Arc::from(s))
}
}
impl std::fmt::Display for StepKindId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum StepPayload {
HurlEntries(String),
MergedAsserts {
lines: usize,
},
Structured(serde_json::Value),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Retry {
pub count: u32,
pub interval_ms: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Guard(pub String);
impl Guard {
pub fn skips(&self) -> bool {
let value = self.0.trim();
value.is_empty() || value.eq_ignore_ascii_case("false") || value == "0"
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct LoweredStep {
pub step: StepRef,
pub kind: StepKindId,
pub payload: StepPayload,
pub optional: bool,
pub when: Option<Guard>,
pub label: Option<String>,
pub save_as: std::collections::BTreeMap<String, String>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct StepBatch {
pub index: usize,
pub engine: EngineId,
pub steps: Vec<LoweredStep>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Status {
Passed,
Failed,
Skipped,
Warned,
}
#[derive(Debug, Clone)]
pub struct StepOutcome {
pub step: StepRef,
pub status: Status,
pub attempts: u32,
pub duration: Duration,
pub detail: Option<String>,
pub attempt_details: Vec<String>,
pub reproduce_hint: Option<String>,
}
#[derive(Debug)]
pub struct BatchResult {
pub steps: Vec<StepOutcome>,
pub error: Option<EngineError>,
}
#[cfg(test)]
mod tests {
use super::Guard;
#[test]
fn guard_skips_on_empty_and_literal_false() {
for skipping in ["", " ", "false", "FALSE", "0"] {
assert!(Guard(skipping.to_owned()).skips(), "{skipping:?}");
}
for running in ["true", "yes", "1", "anything"] {
assert!(!Guard(running.to_owned()).skips(), "{running:?}");
}
}
}