macroonz_compiler/plan/
type_contract.rs1use super::{BoundAxis, InvalidationTrigger, PlanError, PlanIssue};
7use crate::bounded::{Bounded, Capping};
8use crate::diagnostic::{
9 Family, LineBody, Observed, PLANNING_FAMILY, Phase, REPAIR_LIMIT, RefusalClass, Refused, Repair,
10};
11use core::fmt;
12
13impl BoundAxis {
14 #[must_use]
18 pub const fn slot(self) -> u8 {
19 match self {
20 Self::Declarations => 0,
21 Self::Outputs => 1,
22 Self::Triggers => 2,
23 Self::TraceEntries => 3,
24 Self::OriginEdges => 4,
25 }
26 }
27
28 #[must_use]
30 pub const fn described(self) -> &'static str {
31 match self {
32 Self::Declarations => "the captured declarations one account may name",
33 Self::Outputs => "the outputs one plan may declare",
34 Self::Triggers => "the triggers one plan may watch",
35 Self::TraceEntries => "the entries one decision trace may record",
36 Self::OriginEdges => "the edges one origin trail may draw",
37 }
38 }
39}
40
41impl InvalidationTrigger {
42 #[must_use]
44 pub const fn slot(&self) -> u8 {
45 match self {
46 Self::CapturedDeclaration { .. } => 0,
47 Self::Profile { .. } => 1,
48 Self::Generator { .. } => 2,
49 Self::Declared { .. } => 3,
50 Self::ProjectionContent { .. } => 4,
51 }
52 }
53}
54
55impl PlanIssue {
56 #[must_use]
58 pub const fn slot(&self) -> u8 {
59 match self {
60 Self::ContradictoryFacts { .. } => 1,
61 Self::UnknownKind { .. } => 2,
62 Self::ProfileUnsupported { .. } => 3,
63 Self::BoundExceeded { .. } => 4,
64 Self::MembershipIncomplete { .. } => 5,
65 Self::OrphanGeneratedNode { .. } => 6,
66 Self::MembershipDoubled { .. } => 7,
67 Self::TrailDiscontinuous { .. } => 8,
68 Self::CauseSetUnwatchable { .. } => 9,
69 Self::MembershipForeign { .. } => 10,
70 Self::AddressInert { .. } => 11,
71 }
72 }
73
74 #[must_use]
76 pub const fn observed(&self) -> Observed {
77 match self {
78 Self::ContradictoryFacts { .. }
79 | Self::UnknownKind { .. }
80 | Self::MembershipDoubled { .. }
81 | Self::MembershipForeign { .. }
82 | Self::AddressInert { .. } => Observed::ContractDisagreement,
83 Self::ProfileUnsupported { .. } => Observed::ProfileDisagreement,
84 Self::BoundExceeded { .. } | Self::CauseSetUnwatchable { .. } => {
85 Observed::BoundExceeded
86 }
87 Self::MembershipIncomplete { .. } => Observed::SeatAbsent,
88 Self::OrphanGeneratedNode { .. } | Self::TrailDiscontinuous { .. } => {
89 Observed::OriginAbsent
90 }
91 }
92 }
93}
94
95impl fmt::Display for PlanIssue {
96 fn fmt(&self, into: &mut fmt::Formatter<'_>) -> fmt::Result {
97 match self {
98 Self::ContradictoryFacts { between } => write!(
99 into,
100 "two facts that decided this plan disagree: {}.{} and {}.{}",
101 between.left.home, between.left.name, between.right.home, between.right.name
102 ),
103 Self::UnknownKind { .. } => {
104 into.write_str("the plan names a kind this compiler was not handed")
105 }
106 Self::ProfileUnsupported { profile } => write!(
107 into,
108 "the profile {}/{} at version {} offers no such projection",
109 profile.stem(),
110 profile.name(),
111 profile.version().position()
112 ),
113 Self::BoundExceeded {
114 axis,
115 bound,
116 observed,
117 } => write!(
118 into,
119 "{}: {observed} offered where {bound} are declared",
120 axis.described()
121 ),
122 Self::MembershipIncomplete { .. } => {
123 into.write_str("a declared output is absent from the plan's membership")
124 }
125 Self::OrphanGeneratedNode { .. } => {
126 into.write_str("a generated unit arrived with no origin")
127 }
128 Self::MembershipDoubled {
129 role_slot,
130 observed,
131 } => write!(
132 into,
133 "{observed} members stand under the seat at roster position {role_slot}"
134 ),
135 Self::TrailDiscontinuous { at } => write!(
136 into,
137 "the origin trail's edge at position {at} does not start where the edge before it ended"
138 ),
139 Self::CauseSetUnwatchable { named, watchable } => write!(
140 into,
141 "the account names {named} independent causes and this reading watches {watchable}"
142 ),
143 Self::MembershipForeign { seat } => write!(
144 into,
145 "the member under the seat {seat} stands outside the kind's declared roster"
146 ),
147 Self::AddressInert { seat } => write!(
148 into,
149 "an address was stated for the seat {seat}, which no publication act consumes"
150 ),
151 }
152 }
153}
154
155impl fmt::Display for PlanError {
156 fn fmt(&self, into: &mut fmt::Formatter<'_>) -> fmt::Result {
157 write!(into, "{}", self.first_issue())?;
158 let further = self.issues().count().saturating_sub(1);
159 if further > 0 {
160 write!(into, ", and {further} further issues")?;
161 }
162 if let Capping::Truncated { omitted } = self.capping() {
163 write!(into, ", {omitted} of them not carried")?;
164 }
165 Ok(())
166 }
167}
168
169impl core::error::Error for PlanError {}
170
171impl Refused for PlanError {
172 const PHASE: Phase = Phase::Planning;
173 const FAMILY: Family = PLANNING_FAMILY;
174
175 fn class(&self) -> RefusalClass {
176 RefusalClass::PlanNotStated
177 }
178
179 fn first(&self) -> String {
180 self.first_issue().to_string()
181 }
182
183 fn observed(&self) -> Observed {
184 self.first_issue().observed()
185 }
186
187 fn body(&self) -> LineBody {
188 let further = self.issues().count().saturating_sub(1);
189 let capping = self.capping();
190 if further == 0 && capping == Capping::Complete {
191 LineBody::SingleCause
192 } else {
193 LineBody::Body { further, capping }
194 }
195 }
196
197 fn related(&self) -> Vec<Vec<u8>> {
199 self.issues()
200 .iter()
201 .skip(1)
202 .map(PlanIssue::canonical_bytes)
203 .collect()
204 }
205
206 fn repairs(&self) -> Bounded<Repair, REPAIR_LIMIT> {
210 Bounded::empty()
211 }
212}