1use super::{
6 AssemblyPosture, CodecContent, CodecDirection, CodecError, CodecIssue, CodecPlacement,
7 CodecProjection, CodecTypePath, DecodeRefusal,
8};
9use crate::bounded::{Bounded, Capping};
10use crate::diagnostic::{
11 CODEC_DECLARATION_FAMILY, Family, LineBody, Observed, Phase, REPAIR_LIMIT, RefusalClass,
12 Refused, Repair,
13};
14use crate::identity::{OwnerIdentity, encode_bytes, encode_length};
15use crate::kind::{CanonicalContent, Kind, NoQuestions, SoleRole};
16use core::fmt;
17
18impl CanonicalContent for CodecContent {
19 fn encode_content_into(&self, into: &mut Vec<u8>) {
20 encode_path(self.shape.owner(), into);
21 encode_bytes(self.shape.refusal().as_bytes(), into);
22 let assembly = self.shape.assembly();
23 encode_bytes(assembly.road().as_bytes(), into);
24 match assembly.posture() {
25 AssemblyPosture::Total => into.push(0),
26 AssemblyPosture::Checked { refusal } => {
27 into.push(1);
28 encode_path(refusal, into);
29 }
30 }
31 encode_length(self.shape.count(), into);
32 for member in self.shape.members() {
33 let mut encoded = Vec::new();
34 encode_bytes(member.spelling().as_bytes(), &mut encoded);
35 encode_path(member.held_as(), &mut encoded);
36 encode_bytes(member.shape().name().as_bytes(), &mut encoded);
37 encode_bytes(member.cardinality().name().as_bytes(), &mut encoded);
38 encode_bytes(&encoded, into);
39 }
40 encode_bytes(self.direction.name().as_bytes(), into);
41 match &self.placement {
42 CodecPlacement::AtDeclarationSite => into.push(0),
43 CodecPlacement::PublishedModule { spelling } => {
44 into.push(1);
45 encode_bytes(spelling.spelling().as_bytes(), into);
46 }
47 }
48 encode_owner(self.schema.as_ref(), into);
49 encode_owner(self.byte_role.as_ref(), into);
50 encode_length(self.assumptions.len(), into);
51 for assumption in self.assumptions.as_slice() {
52 encode_bytes(&assumption.citation_bytes(), into);
53 }
54 }
55}
56
57fn encode_path(path: &CodecTypePath, into: &mut Vec<u8>) {
58 encode_bytes(path.rooting().name().as_bytes(), into);
59 encode_length(path.count(), into);
60 for segment in path.segments() {
61 encode_bytes(segment.as_bytes(), into);
62 }
63}
64
65fn encode_owner(owner: Option<&OwnerIdentity>, into: &mut Vec<u8>) {
66 match owner {
67 None => into.push(0),
68 Some(identity) => {
69 into.push(1);
70 encode_bytes(&identity.citation_bytes(), into);
71 }
72 }
73}
74
75impl Kind for CodecProjection {
76 const NAME: &'static str = "codec-projection";
77
78 type Content = CodecContent;
79 type Role = SoleRole;
80 type Question = NoQuestions;
81}
82
83impl CodecDirection {
84 #[must_use]
86 pub const fn writes(self) -> bool {
87 match self {
88 Self::Encode | Self::RoundTrip => true,
89 Self::Decode => false,
90 }
91 }
92
93 #[must_use]
99 pub const fn reads(self) -> bool {
100 match self {
101 Self::Decode | Self::RoundTrip => true,
102 Self::Encode => false,
103 }
104 }
105}
106
107impl DecodeRefusal {
108 #[must_use]
112 pub const fn carries_member(self) -> bool {
113 match self {
114 Self::Truncated
115 | Self::LengthPastRemaining
116 | Self::LengthPastAddressableWidth
117 | Self::CountPastDeclaredWidth
118 | Self::TextNotUtf8
119 | Self::MemberNotAdmitted
120 | Self::SlotNotAdmitted
121 | Self::NestedMemberRefused
122 | Self::PresenceNotAdmitted => true,
123 Self::TrailingBytes | Self::NotAssembled => false,
124 }
125 }
126
127 #[must_use]
129 pub const fn sentence(self) -> &'static str {
130 match self {
131 Self::Truncated => "The material ended inside this member.",
132 Self::LengthPastRemaining => {
133 "This member's declared length runs past the material that remains."
134 }
135 Self::LengthPastAddressableWidth => {
136 "This member's declared length does not fit an addressable width."
137 }
138 Self::CountPastDeclaredWidth => {
139 "This member's declared count does not fit the width the member is held at."
140 }
141 Self::TextNotUtf8 => "This member's framed bytes are not UTF-8.",
142 Self::MemberNotAdmitted => "The member's own type refused what was read for it.",
143 Self::SlotNotAdmitted => {
144 "The slot read for this member names no arm of the roster it was declared over."
145 }
146 Self::NestedMemberRefused => {
147 "The nested codec this member carries refused the framed material."
148 }
149 Self::PresenceNotAdmitted => {
150 "This member's presence byte is neither of the two the encode road writes."
151 }
152 Self::TrailingBytes => {
153 "Material remains after the last declared member. A canonical encoding is the \
154 whole of what a value writes, so a longer input is not this value with something \
155 after it."
156 }
157 Self::NotAssembled => {
158 "Every member was read, and the road that assembles them refused. The refusal is \
159 the owner's own, carried exactly."
160 }
161 }
162 }
163}
164
165impl CodecIssue {
166 #[must_use]
170 pub const fn slot(&self) -> u8 {
171 match self {
172 Self::PathSegmentsAbsent => 0,
173 Self::SegmentNotAnIdentifier { .. } => 1,
174 Self::PathSegmentsUnbounded { .. } => 2,
175 Self::MemberSpellingAbsent => 3,
176 Self::MemberSpellingNotAnIdentifier { .. } => 4,
177 Self::MemberSpellingDoubled { .. } => 5,
178 Self::MemberShadowsBinding { .. } => 6,
179 Self::AssemblyRoadAbsent => 7,
180 Self::AssemblyRoadNotAnIdentifier { .. } => 8,
181 Self::RefusalSpellingNotAnIdentifier { .. } => 9,
182 Self::ModuleSpellingNotAnIdentifier { .. } => 10,
183 Self::MembersAbsent => 11,
184 Self::MembersUnbounded { .. } => 12,
185 }
186 }
187
188 #[must_use]
190 pub const fn observed(&self) -> Observed {
191 match self {
192 Self::PathSegmentsAbsent
193 | Self::MemberSpellingAbsent
194 | Self::AssemblyRoadAbsent
195 | Self::MembersAbsent => Observed::SeatAbsent,
196 Self::PathSegmentsUnbounded { .. } | Self::MembersUnbounded { .. } => {
197 Observed::BoundExceeded
198 }
199 Self::SegmentNotAnIdentifier { .. }
200 | Self::MemberSpellingNotAnIdentifier { .. }
201 | Self::MemberSpellingDoubled { .. }
202 | Self::MemberShadowsBinding { .. }
203 | Self::AssemblyRoadNotAnIdentifier { .. }
204 | Self::RefusalSpellingNotAnIdentifier { .. }
205 | Self::ModuleSpellingNotAnIdentifier { .. } => Observed::ContractDisagreement,
206 }
207 }
208
209 #[must_use]
211 pub const fn class(&self) -> RefusalClass {
212 match self {
213 Self::PathSegmentsUnbounded { .. } | Self::MembersUnbounded { .. } => {
214 RefusalClass::MagnitudeNotHeld
215 }
216 Self::PathSegmentsAbsent
217 | Self::SegmentNotAnIdentifier { .. }
218 | Self::MemberSpellingAbsent
219 | Self::MemberSpellingNotAnIdentifier { .. }
220 | Self::MemberSpellingDoubled { .. }
221 | Self::MemberShadowsBinding { .. }
222 | Self::AssemblyRoadAbsent
223 | Self::AssemblyRoadNotAnIdentifier { .. }
224 | Self::RefusalSpellingNotAnIdentifier { .. }
225 | Self::ModuleSpellingNotAnIdentifier { .. }
226 | Self::MembersAbsent => RefusalClass::DeclarationNotRead,
227 }
228 }
229}
230
231impl fmt::Display for CodecIssue {
232 fn fmt(&self, into: &mut fmt::Formatter<'_>) -> fmt::Result {
233 match self {
234 Self::PathSegmentsAbsent => into.write_str("a rendered type path names no segment"),
235 Self::SegmentNotAnIdentifier { segment } => {
236 write!(into, "the path segment {segment} is not one Rust identifier")
237 }
238 Self::PathSegmentsUnbounded { bound, observed } => write!(
239 into,
240 "a rendered type path names {observed} segments where {bound} are declared"
241 ),
242 Self::MemberSpellingAbsent => into.write_str("a codec member states no spelling"),
243 Self::MemberSpellingNotAnIdentifier { spelling } => {
244 write!(into, "the member spelling {spelling} is not one Rust identifier")
245 }
246 Self::MemberSpellingDoubled { spelling } => write!(
247 into,
248 "two members of one shape are both spelled {spelling}, so the decode road would bind one local twice"
249 ),
250 Self::MemberShadowsBinding { spelling, binding } => write!(
251 into,
252 "the member {spelling} is spelled like {binding}, which the decode road binds for itself"
253 ),
254 Self::AssemblyRoadAbsent => into.write_str("a codec assembly road states no spelling"),
255 Self::AssemblyRoadNotAnIdentifier { spelling } => {
256 write!(into, "the assembly road {spelling} is not one Rust identifier")
257 }
258 Self::RefusalSpellingNotAnIdentifier { spelling } => write!(
259 into,
260 "the rendered decode refusal {spelling} is not one Rust identifier"
261 ),
262 Self::ModuleSpellingNotAnIdentifier { spelling } => write!(
263 into,
264 "the published module {spelling} is not one Rust identifier"
265 ),
266 Self::MembersAbsent => into.write_str(
267 "a codec shape declares no member, so its decode road could refuse for one reason and admit every other input",
268 ),
269 Self::MembersUnbounded { bound, observed } => write!(
270 into,
271 "a codec shape declares {observed} members where {bound} are declared"
272 ),
273 }
274 }
275}
276
277impl fmt::Display for CodecError {
278 fn fmt(&self, into: &mut fmt::Formatter<'_>) -> fmt::Result {
279 write!(into, "{}", self.first_issue())?;
280 let further = self.issues().count().saturating_sub(1);
281 if further > 0 {
282 write!(into, ", and {further} further issues")?;
283 }
284 if let Capping::Truncated { omitted } = self.capping() {
285 write!(into, ", {omitted} of them not carried")?;
286 }
287 Ok(())
288 }
289}
290
291impl core::error::Error for CodecError {}
292
293impl Refused for CodecError {
294 const PHASE: Phase = Phase::Capture;
295 const FAMILY: Family = CODEC_DECLARATION_FAMILY;
296
297 fn class(&self) -> RefusalClass {
298 self.first_issue().class()
299 }
300
301 fn first(&self) -> String {
302 self.first_issue().to_string()
303 }
304
305 fn observed(&self) -> Observed {
306 self.first_issue().observed()
307 }
308
309 fn body(&self) -> LineBody {
310 let further = self.issues().count().saturating_sub(1);
311 let capping = self.capping();
312 if further == 0 && capping == Capping::Complete {
313 LineBody::SingleCause
314 } else {
315 LineBody::Body { further, capping }
316 }
317 }
318
319 fn related(&self) -> Vec<Vec<u8>> {
321 self.issues()
322 .iter()
323 .skip(1)
324 .map(CodecIssue::canonical_bytes)
325 .collect()
326 }
327
328 fn repairs(&self) -> Bounded<Repair, REPAIR_LIMIT> {
332 Bounded::empty()
333 }
334}