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}