macroonz_compiler/explanation/
type_contract.rs1use super::encode::answer_material;
7use super::project::human_line;
8use super::{
9 ExplanationError, ExplanationIssue, UNIVERSAL_QUESTION_COUNT, UniversalAnswer,
10 UniversalQuestion,
11};
12use crate::bounded::{Bounded, Capping};
13use crate::diagnostic::{
14 EXPLANATION_FAMILY, Family, LineBody, Observed, Phase, REPAIR_LIMIT, RefusalClass, Refused,
15 Repair,
16};
17use crate::identity::encode_bytes;
18use crate::kind::{Answer, Question};
19use core::fmt;
20
21const _: () = assert!(
22 UNIVERSAL_QUESTION_COUNT == UniversalQuestion::ALL.len(),
23 "the universal seat width and the universal roster are one number, stated twice",
24);
25
26impl Question for UniversalQuestion {
27 const ALL: &'static [Self] = &[
28 Self::WhatAreYou,
29 Self::WhichOwnerRequired,
30 Self::WhichDeclarationCaused,
31 Self::WhichProfile,
32 Self::WhichOutputAndDigest,
33 Self::WhichAssumptions,
34 Self::WhatInvalidates,
35 Self::WhyRelatedNotGenerated,
36 Self::WhatRepairsARefusal,
37 ];
38
39 type Answer = UniversalAnswer;
40
41 fn name(self) -> &'static str {
42 match self {
43 Self::WhatAreYou => "what-are-you",
44 Self::WhichOwnerRequired => "which-owner-required",
45 Self::WhichDeclarationCaused => "which-declaration-caused",
46 Self::WhichProfile => "which-profile",
47 Self::WhichOutputAndDigest => "which-output-and-digest",
48 Self::WhichAssumptions => "which-assumptions",
49 Self::WhatInvalidates => "what-invalidates",
50 Self::WhyRelatedNotGenerated => "why-related-not-generated",
51 Self::WhatRepairsARefusal => "what-repairs-a-refusal",
52 }
53 }
54}
55
56impl UniversalQuestion {
57 #[must_use]
59 pub const fn described(self) -> &'static str {
60 match self {
61 Self::WhatAreYou => "what are you",
62 Self::WhichOwnerRequired => "which owner required you",
63 Self::WhichDeclarationCaused => "which declaration caused you",
64 Self::WhichProfile => "which profile were you decided under",
65 Self::WhichOutputAndDigest => "which output identity and digest are you",
66 Self::WhichAssumptions => "which assumptions do you rest on",
67 Self::WhatInvalidates => "what invalidates you",
68 Self::WhyRelatedNotGenerated => "why was a related projection not generated",
69 Self::WhatRepairsARefusal => "what repairs a refusal",
70 }
71 }
72}
73
74impl Answer for UniversalAnswer {
75 type Question = UniversalQuestion;
76
77 fn question(&self) -> UniversalQuestion {
78 match self {
79 Self::Kind { .. } => UniversalQuestion::WhatAreYou,
80 Self::Owner { .. } => UniversalQuestion::WhichOwnerRequired,
81 Self::CausingDeclarations { .. } => UniversalQuestion::WhichDeclarationCaused,
82 Self::Profile { .. } => UniversalQuestion::WhichProfile,
83 Self::OutputAndDigest { .. } => UniversalQuestion::WhichOutputAndDigest,
84 Self::Assumptions { .. } => UniversalQuestion::WhichAssumptions,
85 Self::Invalidators { .. } => UniversalQuestion::WhatInvalidates,
86 Self::RelatedDispositions { .. } => UniversalQuestion::WhyRelatedNotGenerated,
87 Self::Repairs { .. } => UniversalQuestion::WhatRepairsARefusal,
88 }
89 }
90
91 fn encode_into(&self, into: &mut Vec<u8>) {
92 into.push(self.slot());
93 let mut material = Vec::new();
94 answer_material(self, &mut material);
95 encode_bytes(&material, into);
96 }
97
98 fn human(&self) -> String {
99 human_line(self)
100 }
101}
102
103impl UniversalAnswer {
104 #[must_use]
110 pub const fn slot(&self) -> u8 {
111 match self {
112 Self::Kind { .. } => 0,
113 Self::Owner { .. } => 1,
114 Self::CausingDeclarations { .. } => 2,
115 Self::Profile { .. } => 3,
116 Self::OutputAndDigest { .. } => 4,
117 Self::Assumptions { .. } => 5,
118 Self::Invalidators { .. } => 6,
119 Self::RelatedDispositions { .. } => 7,
120 Self::Repairs { .. } => 8,
121 }
122 }
123}
124
125impl ExplanationIssue {
126 #[must_use]
128 pub const fn slot(&self) -> u8 {
129 match self {
130 Self::UniversalUnanswered { .. } => 0,
131 Self::UniversalAnsweredTwice { .. } => 1,
132 Self::DeclaredUnanswered { .. } => 2,
133 Self::DeclaredAnsweredTwice { .. } => 3,
134 Self::QuestionOutsideRoster { .. } => 4,
135 Self::SeatBoundExceeded { .. } => 5,
136 Self::OutputsBesideTheProof { .. } => 6,
137 }
138 }
139
140 #[must_use]
142 pub const fn observed(&self) -> Observed {
143 match self {
144 Self::UniversalUnanswered { .. } | Self::DeclaredUnanswered { .. } => {
145 Observed::SeatAbsent
146 }
147 Self::UniversalAnsweredTwice { .. }
148 | Self::DeclaredAnsweredTwice { .. }
149 | Self::QuestionOutsideRoster { .. }
150 | Self::OutputsBesideTheProof { .. } => Observed::ContractDisagreement,
151 Self::SeatBoundExceeded { .. } => Observed::BoundExceeded,
152 }
153 }
154}
155
156impl fmt::Display for ExplanationIssue {
157 fn fmt(&self, into: &mut fmt::Formatter<'_>) -> fmt::Result {
158 match self {
159 Self::UniversalUnanswered { question } => write!(
160 into,
161 "the universal question \"{}\" has no answer",
162 question.described()
163 ),
164 Self::UniversalAnsweredTwice { question } => write!(
165 into,
166 "the universal question \"{}\" was answered more than once",
167 question.described()
168 ),
169 Self::DeclaredUnanswered { question, slot } => write!(
170 into,
171 "the kind's question \"{question}\" at position {slot} has no answer"
172 ),
173 Self::DeclaredAnsweredTwice { question, slot } => write!(
174 into,
175 "the kind's question \"{question}\" at position {slot} was answered twice or more"
176 ),
177 Self::QuestionOutsideRoster { question } => write!(
178 into,
179 "an answer names the question \"{question}\", which its own roster does not carry"
180 ),
181 Self::SeatBoundExceeded { bound, observed } => {
182 write!(into, "{observed} seats offered where {bound} are declared")
183 }
184 Self::OutputsBesideTheProof {
185 expected,
186 observed,
187 diverges,
188 } => write!(
189 into,
190 "the output answer carries {observed} rows beside the proof's {expected}, diverging at roster position {diverges}"
191 ),
192 }
193 }
194}
195
196impl fmt::Display for ExplanationError {
197 fn fmt(&self, into: &mut fmt::Formatter<'_>) -> fmt::Result {
198 write!(into, "{}", self.first_issue())?;
199 let further = self.issues().count().saturating_sub(1);
200 if further > 0 {
201 write!(into, ", and {further} further issues")?;
202 }
203 if let Capping::Truncated { omitted } = self.capping() {
204 write!(into, ", {omitted} of them not carried")?;
205 }
206 Ok(())
207 }
208}
209
210impl core::error::Error for ExplanationError {}
211
212impl Refused for ExplanationError {
213 const PHASE: Phase = Phase::Explanation;
214 const FAMILY: Family = EXPLANATION_FAMILY;
215
216 fn class(&self) -> RefusalClass {
217 RefusalClass::ExplanationNotCovered
218 }
219
220 fn first(&self) -> String {
221 self.first_issue().to_string()
222 }
223
224 fn observed(&self) -> Observed {
225 self.first_issue().observed()
226 }
227
228 fn body(&self) -> LineBody {
229 let further = self.issues().count().saturating_sub(1);
230 let capping = self.capping();
231 if further == 0 && capping == Capping::Complete {
232 LineBody::SingleCause
233 } else {
234 LineBody::Body { further, capping }
235 }
236 }
237
238 fn related(&self) -> Vec<Vec<u8>> {
240 self.issues()
241 .iter()
242 .skip(1)
243 .map(ExplanationIssue::canonical_bytes)
244 .collect()
245 }
246
247 fn repairs(&self) -> Bounded<Repair, REPAIR_LIMIT> {
251 Bounded::empty()
252 }
253}