varar_core/failure.rs
1//! Converts a caught step failure into the structured [`ExampleFailure`] payload
2//! — port of `failure.ts` / `Failure.java`. The Java stack-trace-scraping
3//! machinery becomes a structural [`FailureLocation`] lookup by exact path match.
4
5use crate::error::{StepError, StepFailure};
6use crate::result::{CellFailure, ExampleFailure};
7
8/// A caught step failure → the `ExampleResult.failure` payload. `fallback_line`
9/// is used when `failure` carries no location matching `oath_path`.
10pub fn to_failure(failure: &StepFailure, oath_path: &str, fallback_line: i64) -> ExampleFailure {
11 let message = failure.error.message();
12
13 let cells = match &failure.error {
14 StepError::CellMismatch(cells) => {
15 let failing: Vec<CellFailure> = cells
16 .iter()
17 .filter(|c| !c.ok)
18 .map(|c| CellFailure::new(c.span.start_offset, c.span.end_offset, c.actual.clone()))
19 .collect();
20 (!failing.is_empty()).then_some(failing)
21 }
22 _ => None,
23 };
24
25 // The location's path is the oath, or — for a step a reference block
26 // spliced in (ADR 0016) — the document that step was written in. Either way
27 // its line and anchor are the precise ones; `doc_path` says which file they
28 // address.
29 let here = failure.location.as_ref();
30 let line = here.map_or(fallback_line, |l| l.line as i64);
31 let doc_path = here.filter(|l| l.path != oath_path).map(|l| l.path.clone());
32 // The executor recorded the anchor alongside the location, so this is the
33 // failing step's span (or the first mismatched cell's) — what a renderer
34 // underlines instead of the whole line. `None` when the failure carries no
35 // location at all, i.e. it never passed through a step.
36 let anchor = here.map(|l| l.anchor);
37
38 let stack = render_stack(failure);
39 ExampleFailure {
40 line,
41 message,
42 stack,
43 cells,
44 anchor,
45 doc_path,
46 }
47}
48
49/// Display-only rendering of the failure's location (the Java `stack` field is
50/// rendered from structural data, not scraped from it).
51fn render_stack(failure: &StepFailure) -> String {
52 match &failure.location {
53 Some(l) => {
54 format!("{}\n at {} ({}:{})", failure.error.message(), l.label, l.path, l.line)
55 }
56 None => failure.error.message(),
57 }
58}