Skip to main content

rustev_eval/
acquisition_replay.rs

1//! Offline acquisition supplies, checked against the pending request and run.
2use rustev_contract::Document;
3use rustev_contract::acquisition::AcquisitionPage;
4use rustev_contract::acquisition_record::AcquisitionResult;
5use rustev_contract::ids::AcquisitionRequestId;
6use rustev_contract::judgment::Unresolved;
7use rustev_contract::replay::{AcquisitionSupply, ItemLocation};
8use rustev_contract::run::RunRecord;
9use rustev_core::evaluate::Evaluation;
10
11use crate::replay::{Incomparable, Inconsistency};
12
13/// An uncaptured request, with its recomputed identity.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct MissingAcquisition {
16    pub step: String,
17    pub tier: u32,
18    pub page: u32,
19    pub request: AcquisitionRequestId,
20}
21
22#[derive(Debug, Clone)]
23pub(crate) enum PageValue {
24    Page(AcquisitionPage),
25    Reason(Unresolved),
26}
27
28fn bad(index: usize, what: Inconsistency) -> Incomparable {
29    Incomparable::Inconsistent {
30        location: ItemLocation::AcquisitionPage(index),
31        what,
32    }
33}
34
35pub(crate) fn supply_page(
36    ev: &mut Evaluation<'_>,
37    entry: &AcquisitionSupply,
38    value: &PageValue,
39    run: &RunRecord,
40    index: usize,
41) -> Result<(), Incomparable> {
42    let pending = ev
43        .pending_acquisitions()
44        .into_iter()
45        .find(|p| p.step == entry.step && p.tier_index == entry.tier && p.page_index == entry.page)
46        .ok_or_else(|| bad(index, Inconsistency::NotPending))?;
47    if pending.request_id != entry.request {
48        return Err(bad(index, Inconsistency::RequestIdentity));
49    }
50    let record = run
51        .acquisitions
52        .iter()
53        .flatten()
54        .find(|p| p.step == entry.step && p.tier == entry.tier && p.page == entry.page)
55        .ok_or_else(|| bad(index, Inconsistency::NotInRun))?;
56    if record.request != entry.request {
57        return Err(bad(index, Inconsistency::RequestIdentity));
58    }
59    if record.attempts.last().map(|a| &a.attempt_id) != entry.attempt_id.as_ref() {
60        return Err(bad(index, Inconsistency::Origin));
61    }
62    match (value, &record.result) {
63        (PageValue::Page(page), AcquisitionResult::Page) => {
64            let bytes = page
65                .record_canonical()
66                .map_err(|_| bad(index, Inconsistency::NoRecordForm))?;
67            if page.request != pending.request_id {
68                return Err(bad(index, Inconsistency::RequestIdentity));
69            }
70            ev.supply_page(&entry.step, entry.tier, entry.page, &bytes)
71        }
72        (PageValue::Reason(reason), AcquisitionResult::Failed(recorded)) if reason == recorded => {
73            ev.supply_acquisition_failure(&entry.step, entry.tier, entry.page, reason.clone())
74        }
75        _ => return Err(bad(index, Inconsistency::Value)),
76    }
77    .map_err(|_| bad(index, Inconsistency::OutputRefused))
78}
79
80pub(crate) fn fill_missing(
81    ev: &mut Evaluation<'_>,
82    entries: &[AcquisitionSupply],
83    missing: &mut Vec<MissingAcquisition>,
84) -> Result<(), Incomparable> {
85    loop {
86        let absent = ev.pending_acquisitions().into_iter().find(|p| {
87            !entries
88                .iter()
89                .any(|e| e.step == p.step && e.tier == p.tier_index && e.page == p.page_index)
90        });
91        let Some(p) = absent else {
92            return Ok(());
93        };
94        ev.supply_acquisition_failure(
95            &p.step,
96            p.tier_index,
97            p.page_index,
98            Unresolved::MissingEvidence {
99                fields: vec![p.step.clone()],
100            },
101        )
102        // Unreachable for a fresh pending request: no mutation intervenes, and
103        // MissingEvidence with exactly this step is accepted by acquire and widen.
104        // This uncaptured page has no acquisition_pages index to report.
105        .map_err(|_| bad(0, Inconsistency::NotPending))?;
106        missing.push(MissingAcquisition {
107            step: p.step,
108            tier: p.tier_index,
109            page: p.page_index,
110            request: p.request_id,
111        });
112    }
113}