Skip to main content

macroonz_compiler/closure/
type_contract.rs

1//! The constant answers this home's issue roster settles, and the contracts a closure refusal stands under.
2//!
3//! Each table is total, so an issue admitted later stops the compiler in every one of them until somebody says what that row's position, sentence, class, and classification are.
4//! Nothing here decides anything: the pass that establishes an issue is `prove.rs`, and the proving is `type_guard.rs`.
5
6use super::{ClosureError, ClosureIssue};
7use crate::bounded::{Bounded, Capping};
8use crate::diagnostic::{
9    CLOSURE_FAMILY, Family, LineBody, Observed, Phase, REPAIR_LIMIT, RefusalClass, Refused, Repair,
10};
11use crate::kind::Role;
12use core::fmt;
13
14impl<R: Role> ClosureIssue<R> {
15    /// This row's position in the declared roster, written ahead of the issue's own material.
16    ///
17    /// Appended and never renumbered: the byte stands inside every identity derived over a refusal that carries it.
18    #[must_use]
19    pub const fn slot(&self) -> u8 {
20        match self {
21            Self::MemberMissing { .. } => 0,
22            Self::MemberUnplanned { .. } => 1,
23            Self::MemberDuplicated { .. } => 2,
24            Self::OriginOrphan { .. } => 3,
25            Self::DigestMismatch { .. } => 4,
26            Self::SemanticKeyMismatch { .. } => 5,
27            Self::MaterializationMismatch { .. } => 6,
28            Self::MemberPlannedTwice { .. } => 7,
29            Self::MembershipDisagreement { .. } => 8,
30            Self::ReconstructionEmpty => 9,
31            Self::ReconstructionUndeclarable { .. } => 10,
32            Self::JoinedTreeUnbounded { .. } => 11,
33            Self::ArtifactAddressDoubled { .. } => 12,
34            Self::ArtifactAddressAbsent { .. } => 13,
35        }
36    }
37
38    /// The seat this issue was established at, where it is about one.
39    #[must_use]
40    pub const fn role(&self) -> Option<R> {
41        match self {
42            Self::MemberMissing { role }
43            | Self::MemberUnplanned { role }
44            | Self::MemberDuplicated { role, .. }
45            | Self::OriginOrphan { role }
46            | Self::DigestMismatch { role }
47            | Self::SemanticKeyMismatch { role }
48            | Self::MaterializationMismatch { role }
49            | Self::MemberPlannedTwice { role, .. }
50            | Self::MembershipDisagreement { role }
51            | Self::ArtifactAddressDoubled { role, .. }
52            | Self::ArtifactAddressAbsent { role } => Some(*role),
53            Self::ReconstructionEmpty
54            | Self::ReconstructionUndeclarable { .. }
55            | Self::JoinedTreeUnbounded { .. } => None,
56        }
57    }
58
59    /// How what this issue observed differs from the contract that was expected.
60    #[must_use]
61    pub const fn observed(&self) -> Observed {
62        match self {
63            Self::MemberMissing { .. }
64            | Self::ReconstructionEmpty
65            | Self::ArtifactAddressAbsent { .. } => Observed::SeatAbsent,
66            Self::MemberUnplanned { .. }
67            | Self::MemberDuplicated { .. }
68            | Self::MemberPlannedTwice { .. }
69            | Self::MembershipDisagreement { .. }
70            | Self::ArtifactAddressDoubled { .. } => Observed::ContractDisagreement,
71            Self::OriginOrphan { .. } => Observed::OriginAbsent,
72            Self::DigestMismatch { .. } | Self::SemanticKeyMismatch { .. } => {
73                Observed::IdentityDisagreement
74            }
75            Self::MaterializationMismatch { .. } => Observed::ProfileDisagreement,
76            Self::ReconstructionUndeclarable { .. } | Self::JoinedTreeUnbounded { .. } => {
77                Observed::BoundExceeded
78            }
79        }
80    }
81
82    /// Which class of refusal a line opening with this issue is about.
83    ///
84    /// Two rows are magnitudes rather than disagreements: what they report is a rendering that would have passed a declared bound, and the seats it filled are not in question.
85    #[must_use]
86    pub const fn class(&self) -> RefusalClass {
87        match self {
88            Self::ReconstructionUndeclarable { .. } | Self::JoinedTreeUnbounded { .. } => {
89                RefusalClass::MagnitudeNotHeld
90            }
91            Self::MemberMissing { .. }
92            | Self::MemberUnplanned { .. }
93            | Self::MemberDuplicated { .. }
94            | Self::OriginOrphan { .. }
95            | Self::DigestMismatch { .. }
96            | Self::SemanticKeyMismatch { .. }
97            | Self::MaterializationMismatch { .. }
98            | Self::MemberPlannedTwice { .. }
99            | Self::MembershipDisagreement { .. }
100            | Self::ReconstructionEmpty
101            | Self::ArtifactAddressDoubled { .. }
102            | Self::ArtifactAddressAbsent { .. } => RefusalClass::RenderingNotClosed,
103        }
104    }
105}
106
107impl<R: Role> fmt::Display for ClosureIssue<R> {
108    fn fmt(&self, into: &mut fmt::Formatter<'_>) -> fmt::Result {
109        match self {
110            Self::MemberMissing { role } => {
111                let seat = role.name();
112                write!(
113                    into,
114                    "the plan declares a member at {seat} and nothing rendered one"
115                )
116            }
117            Self::MemberUnplanned { role } => {
118                let seat = role.name();
119                write!(
120                    into,
121                    "a unit was rendered at {seat} and the plan declares none"
122                )
123            }
124            Self::MemberDuplicated { role, observed } => {
125                let seat = role.name();
126                write!(into, "{observed} units were rendered at {seat}")
127            }
128            Self::OriginOrphan { role } => {
129                let seat = role.name();
130                write!(
131                    into,
132                    "the unit at {seat} walks back to an origin the plan did not declare"
133                )
134            }
135            Self::DigestMismatch { role } => {
136                let seat = role.name();
137                write!(
138                    into,
139                    "the digest at {seat} is not the digest of the bytes that unit rendered"
140                )
141            }
142            Self::SemanticKeyMismatch { role } => {
143                let seat = role.name();
144                write!(
145                    into,
146                    "the unit at {seat} answers to a semantic key the plan declared elsewhere"
147                )
148            }
149            Self::MaterializationMismatch { role } => {
150                let seat = role.name();
151                write!(
152                    into,
153                    "the unit at {seat} names a profile or an address the plan did not declare"
154                )
155            }
156            Self::MemberPlannedTwice { role, observed } => {
157                let seat = role.name();
158                write!(
159                    into,
160                    "the plan itself declares {observed} members at {seat}"
161                )
162            }
163            Self::MembershipDisagreement { role } => {
164                let seat = role.name();
165                write!(
166                    into,
167                    "the rebuilt membership and the planned one are not the same set at {seat}"
168                )
169            }
170            Self::ReconstructionEmpty => into.write_str("the rebuild produced no member at all"),
171            Self::ReconstructionUndeclarable { observed } => write!(
172                into,
173                "the {observed} rebuilt members will not declare as a complete output set"
174            ),
175            Self::JoinedTreeUnbounded { destination } => {
176                let delivery = destination.name();
177                write!(
178                    into,
179                    "the tokens joined for {delivery} outgrow the declared magnitude"
180                )
181            }
182            Self::ArtifactAddressDoubled { role, address } => {
183                let seat = role.name();
184                let subject = address.subject;
185                write!(
186                    into,
187                    "the artifact at {seat} stands at an address under {subject} already taken"
188                )
189            }
190            Self::ArtifactAddressAbsent { role } => {
191                let seat = role.name();
192                write!(
193                    into,
194                    "the unit at {seat} is delivered to an address and the plan names none"
195                )
196            }
197        }
198    }
199}
200
201impl<R: Role> fmt::Display for ClosureError<R> {
202    fn fmt(&self, into: &mut fmt::Formatter<'_>) -> fmt::Result {
203        write!(into, "{}", self.first_issue())?;
204        let further = self.issues().count().saturating_sub(1);
205        if further > 0 {
206            write!(into, ", and {further} further issues")?;
207        }
208        if let Capping::Truncated { omitted } = self.capping() {
209            write!(into, ", {omitted} of them not carried")?;
210        }
211        Ok(())
212    }
213}
214
215impl<R: Role> core::error::Error for ClosureError<R> {}
216
217impl<R: Role> Refused for ClosureError<R> {
218    const PHASE: Phase = Phase::Closure;
219    const FAMILY: Family = CLOSURE_FAMILY;
220
221    fn class(&self) -> RefusalClass {
222        self.first_issue().class()
223    }
224
225    fn first(&self) -> String {
226        self.first_issue().to_string()
227    }
228
229    fn observed(&self) -> Observed {
230        self.first_issue().observed()
231    }
232
233    fn body(&self) -> LineBody {
234        let further = self.issues().count().saturating_sub(1);
235        let capping = self.capping();
236        if further == 0 && capping == Capping::Complete {
237            LineBody::SingleCause
238        } else {
239            LineBody::Body { further, capping }
240        }
241    }
242
243    /// The issues established beyond the primary cause; the primary is the summary's own subject, never a member of its related set.
244    fn related(&self) -> Vec<Vec<u8>> {
245        self.issues()
246            .iter()
247            .skip(1)
248            .map(ClosureIssue::canonical_bytes)
249            .collect()
250    }
251
252    /// This home declares no repair of its own.
253    ///
254    /// Every issue above is about what the caller's own plan declared or what the caller's own renderer produced, so the repair is one of those two declarations; a sentence composed here would be this compiler citing a fact nobody declared.
255    fn repairs(&self) -> Bounded<Repair, REPAIR_LIMIT> {
256        Bounded::empty()
257    }
258}