Skip to main content

cuke_dedup/source_adapter/
finalization.rs

1//! Typed authority boundaries for frontend completion.
2
3use super::SourceLanguage;
4use crate::model::{SourceLocation, StepDefinition};
5use std::path::PathBuf;
6
7/// The broadest source authority affected by unresolved static evidence.
8#[non_exhaustive]
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum UncertaintyScope {
11    /// One handler or registration.
12    Definition(SourceLocation),
13    /// One source, only when frontend evidence proves its effects cannot escape.
14    File(PathBuf),
15    /// Every registration in a comparison domain; source-local syntax alone cannot narrow this.
16    Registry(SourceLanguage),
17}
18
19/// What authority cannot be established without executing project code.
20#[non_exhaustive]
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum UncertaintyCause {
23    /// Registration ownership or execution effects may change the registry.
24    Registration,
25    /// Handler values or execution effects cannot establish equivalence.
26    Handler,
27    /// Source or dependency evidence is missing, without proof of registration invalidation.
28    Source,
29}
30
31/// One conservative frontend limitation, independent of its diagnostic wording.
32#[non_exhaustive]
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct SourceUncertainty {
35    /// Authority affected by this limitation.
36    pub scope: UncertaintyScope,
37    /// Missing proof and its effect on comparisons.
38    pub cause: UncertaintyCause,
39    /// Human-readable diagnostic.
40    pub message: String,
41}
42
43impl SourceUncertainty {
44    /// Creates an explicitly scoped limitation; narrowing requires frontend isolation evidence.
45    pub fn new(
46        scope: UncertaintyScope,
47        cause: UncertaintyCause,
48        message: impl Into<String>,
49    ) -> Self {
50        Self {
51            scope,
52            cause,
53            message: message.into(),
54        }
55    }
56
57    /// Whether `definition` falls in this scope: an equal full location span, an equal file path,
58    /// or the comparison domain of the registry language.
59    fn affects(&self, definition: &StepDefinition) -> bool {
60        match &self.scope {
61            UncertaintyScope::Definition(location) => &definition.location == location,
62            UncertaintyScope::File(path) => &definition.location.path == path,
63            UncertaintyScope::Registry(language) => {
64                definition.analysis_profile().comparison_domain == language.comparison_domain()
65            }
66        }
67    }
68}
69
70/// Optional explanation of a frontend trust decision, without authority or completeness effects.
71#[non_exhaustive]
72#[derive(Debug, Clone, PartialEq, Eq)]
73pub struct SourceAdvisory {
74    /// Source decision being explained.
75    pub location: SourceLocation,
76    /// Explanation for callers that opt into frontend diagnostics.
77    pub message: String,
78}
79
80impl SourceAdvisory {
81    /// Creates an advisory; use [`SourceUncertainty`] for unresolved analysis completeness.
82    pub fn new(location: SourceLocation, message: impl Into<String>) -> Self {
83        Self {
84            location,
85            message: message.into(),
86        }
87    }
88}
89
90/// Cross-source limitations collected after extraction, before shared analysis.
91#[non_exhaustive]
92#[derive(Debug, Clone, Default, PartialEq, Eq)]
93pub struct SourceFinalization {
94    /// Limitations remain incomplete even when independent definitions can be retained.
95    pub uncertainties: Vec<SourceUncertainty>,
96    /// Optional frontend advisories for embedders; applying finalization does not emit them.
97    /// Advisory diagnostics do not remove authority or affect completeness.
98    pub advisories: Vec<SourceAdvisory>,
99}
100
101impl SourceFinalization {
102    /// Applies only the authority removal declared by the frontend; uncertainty never grants trust.
103    pub fn apply(&self, definitions: &mut Vec<StepDefinition>) {
104        for uncertainty in &self.uncertainties {
105            match uncertainty.cause {
106                UncertaintyCause::Registration => {
107                    definitions.retain(|definition| !uncertainty.affects(definition))
108                }
109                UncertaintyCause::Handler => {
110                    for definition in definitions
111                        .iter_mut()
112                        .filter(|definition| uncertainty.affects(definition))
113                    {
114                        definition.handler.comparable = false;
115                    }
116                }
117                UncertaintyCause::Source => {}
118            }
119        }
120    }
121}