1use super::spell::{
6 CANDIDATE_BINDING, CARRIED_BINDING, CHOSEN_BINDING, COLLECTED_BINDING, ELECTED_BINDING,
7 INTO_BINDING, LENGTH_BINDING, MATERIAL_BINDING, NESTED_BINDING, PRESENT_BINDING,
8 REMAINING_BINDING, WIDTH_BINDING,
9};
10use super::{
11 AssemblyPosture, CodecContent, CodecDirection, CodecError, CodecIssue, CodecMemberShape,
12 CodecPlacement, CodecProjection, CodecTypePath, DECODE_ROAD, DecodeRefusal, ENCODE_ROAD,
13 MemberContract, ROSTER_CONSTANT, SLOT_ROAD,
14};
15use crate::bounded::{Bounded, Capping};
16use crate::diagnostic::{
17 CODEC_DECLARATION_FAMILY, Family, LineBody, Observed, Phase, REPAIR_LIMIT, RefusalClass,
18 Refused, Repair,
19};
20use crate::identity::{OwnerIdentity, encode_bytes, encode_length};
21use crate::kind::{CanonicalContent, Kind, NoQuestions, SoleRole};
22use core::fmt;
23
24impl CanonicalContent for CodecContent {
25 fn encode_content_into(&self, into: &mut Vec<u8>) {
26 encode_path(self.shape.owner(), into);
27 encode_bytes(self.shape.refusal().as_bytes(), into);
28 let assembly = self.shape.assembly();
29 encode_bytes(assembly.road().as_bytes(), into);
30 match assembly.posture() {
31 AssemblyPosture::Total => into.push(0),
32 AssemblyPosture::Checked { refusal } => {
33 into.push(1);
34 encode_path(refusal, into);
35 }
36 }
37 encode_length(self.shape.count(), into);
38 for member in self.shape.members() {
39 let mut encoded = Vec::new();
40 encode_bytes(member.spelling().as_bytes(), &mut encoded);
41 encode_path(member.held_as(), &mut encoded);
42 encode_bytes(member.shape().name().as_bytes(), &mut encoded);
43 encode_bytes(member.cardinality().name().as_bytes(), &mut encoded);
44 encode_bytes(&encoded, into);
45 }
46 encode_bytes(self.direction.name().as_bytes(), into);
47 match &self.placement {
48 CodecPlacement::AtDeclarationSite => into.push(0),
49 CodecPlacement::PublishedModule { spelling } => {
50 into.push(1);
51 encode_bytes(spelling.spelling().as_bytes(), into);
52 }
53 }
54 encode_owner(self.schema.as_ref(), into);
55 encode_owner(self.byte_role.as_ref(), into);
56 encode_length(self.assumptions.len(), into);
57 for assumption in self.assumptions.as_slice() {
58 encode_bytes(&assumption.citation_bytes(), into);
59 }
60 }
61}
62
63fn encode_path(path: &CodecTypePath, into: &mut Vec<u8>) {
64 encode_bytes(path.rooting().name().as_bytes(), into);
65 encode_length(path.count(), into);
66 for segment in path.segments() {
67 encode_bytes(segment.as_bytes(), into);
68 }
69}
70
71fn encode_owner(owner: Option<&OwnerIdentity>, into: &mut Vec<u8>) {
72 match owner {
73 None => into.push(0),
74 Some(identity) => {
75 into.push(1);
76 encode_bytes(&identity.citation_bytes(), into);
77 }
78 }
79}
80
81impl Kind for CodecProjection {
82 const NAME: &'static str = "codec-projection";
83
84 type Content = CodecContent;
85 type Role = SoleRole;
86 type Question = NoQuestions;
87}
88
89impl CodecDirection {
90 #[must_use]
92 pub const fn writes(self) -> bool {
93 match self {
94 Self::Encode | Self::RoundTrip => true,
95 Self::Decode => false,
96 }
97 }
98
99 #[must_use]
105 pub const fn reads(self) -> bool {
106 match self {
107 Self::Decode | Self::RoundTrip => true,
108 Self::Encode => false,
109 }
110 }
111}
112
113impl DecodeRefusal {
114 #[must_use]
118 pub const fn carries_member(self) -> bool {
119 match self {
120 Self::Truncated
121 | Self::LengthPastRemaining
122 | Self::LengthPastAddressableWidth
123 | Self::CountPastDeclaredWidth
124 | Self::TextNotUtf8
125 | Self::MemberNotAdmitted
126 | Self::SlotNotAdmitted
127 | Self::NestedMemberRefused
128 | Self::PresenceNotAdmitted => true,
129 Self::TrailingBytes | Self::NotAssembled => false,
130 }
131 }
132
133 #[must_use]
135 pub const fn sentence(self) -> &'static str {
136 match self {
137 Self::Truncated => "The material ended inside this member.",
138 Self::LengthPastRemaining => {
139 "This member's declared length runs past the material that remains."
140 }
141 Self::LengthPastAddressableWidth => {
142 "This member's declared length does not fit an addressable width."
143 }
144 Self::CountPastDeclaredWidth => {
145 "This member's declared count does not fit the width the member is held at."
146 }
147 Self::TextNotUtf8 => "This member's framed bytes are not UTF-8.",
148 Self::MemberNotAdmitted => "The member's own type refused what was read for it.",
149 Self::SlotNotAdmitted => {
150 "The slot read for this member names no arm of the roster it was declared over."
151 }
152 Self::NestedMemberRefused => {
153 "The nested codec this member carries refused the framed material."
154 }
155 Self::PresenceNotAdmitted => {
156 "This member's presence byte is neither of the two the encode road writes."
157 }
158 Self::TrailingBytes => {
159 "Material remains after the last declared member. A canonical encoding is the \
160 whole of what a value writes, so a longer input is not this value with something \
161 after it."
162 }
163 Self::NotAssembled => {
164 "Every member was read, and the road that assembles them refused. The refusal is \
165 the owner's own, carried exactly."
166 }
167 }
168 }
169}
170
171pub const MEMBER_CONTRACT: [MemberContract; 5] = [
177 COUNT_CONTRACT.bill,
178 BYTES_CONTRACT.bill,
179 TEXT_CONTRACT.bill,
180 CLOSED_CHOICE_CONTRACT.bill,
181 NESTED_CONTRACT.bill,
182];
183
184#[derive(Clone, Copy)]
186pub(super) enum WriteRoad {
187 Count,
189 Bytes,
191 Text,
193 ClosedChoice,
195 Nested,
197}
198
199#[derive(Clone, Copy)]
201pub(super) enum ReadRoad {
202 Count,
204 Bytes,
206 Text,
208 ClosedChoice,
210 Nested,
212}
213
214#[derive(Clone, Copy)]
216pub(super) struct RenderingContract {
217 pub(super) bill: MemberContract,
219 pub(super) write: WriteRoad,
221 pub(super) read: ReadRoad,
223}
224
225const COUNT_CONTRACT: RenderingContract = RenderingContract {
226 bill: MemberContract {
227 shape: CodecMemberShape::Count,
228 encode_road: "u64::from",
229 decode_road: "<T as ::core::convert::TryFrom<u64>>::try_from",
230 },
231 write: WriteRoad::Count,
232 read: ReadRoad::Count,
233};
234
235const BYTES_CONTRACT: RenderingContract = RenderingContract {
236 bill: MemberContract {
237 shape: CodecMemberShape::Bytes,
238 encode_road: "<T as ::core::convert::AsRef<[u8]>>::as_ref",
239 decode_road: "<T as ::core::convert::TryFrom<::std::vec::Vec<u8>>>::try_from",
240 },
241 write: WriteRoad::Bytes,
242 read: ReadRoad::Bytes,
243};
244
245const TEXT_CONTRACT: RenderingContract = RenderingContract {
246 bill: MemberContract {
247 shape: CodecMemberShape::Text,
248 encode_road: "<T as ::core::convert::AsRef<str>>::as_ref",
249 decode_road: "<T as ::core::convert::TryFrom<::std::string::String>>::try_from",
250 },
251 write: WriteRoad::Text,
252 read: ReadRoad::Text,
253};
254
255const CLOSED_CHOICE_CONTRACT: RenderingContract = RenderingContract {
256 bill: MemberContract {
257 shape: CodecMemberShape::ClosedChoice,
258 encode_road: SLOT_ROAD,
259 decode_road: ROSTER_CONSTANT,
260 },
261 write: WriteRoad::ClosedChoice,
262 read: ReadRoad::ClosedChoice,
263};
264
265const NESTED_CONTRACT: RenderingContract = RenderingContract {
266 bill: MemberContract {
267 shape: CodecMemberShape::Nested,
268 encode_road: ENCODE_ROAD,
269 decode_road: DECODE_ROAD,
270 },
271 write: WriteRoad::Nested,
272 read: ReadRoad::Nested,
273};
274
275pub(super) const fn rendering_contract(shape: CodecMemberShape) -> RenderingContract {
279 match shape {
280 CodecMemberShape::Count => COUNT_CONTRACT,
281 CodecMemberShape::Bytes => BYTES_CONTRACT,
282 CodecMemberShape::Text => TEXT_CONTRACT,
283 CodecMemberShape::ClosedChoice => CLOSED_CHOICE_CONTRACT,
284 CodecMemberShape::Nested => NESTED_CONTRACT,
285 }
286}
287
288pub const RESERVED_BINDINGS: [&str; 12] = [
298 MATERIAL_BINDING,
299 REMAINING_BINDING,
300 INTO_BINDING,
301 NESTED_BINDING,
302 COLLECTED_BINDING,
303 CANDIDATE_BINDING,
304 CHOSEN_BINDING,
305 ELECTED_BINDING,
306 PRESENT_BINDING,
307 CARRIED_BINDING,
308 LENGTH_BINDING,
309 WIDTH_BINDING,
310];
311
312impl CodecIssue {
313 #[must_use]
317 pub const fn slot(&self) -> u8 {
318 match self {
319 Self::PathSegmentsAbsent => 0,
320 Self::SegmentNotAnIdentifier { .. } => 1,
321 Self::PathSegmentsUnbounded { .. } => 2,
322 Self::MemberSpellingAbsent => 3,
323 Self::MemberSpellingNotAnIdentifier { .. } => 4,
324 Self::MemberSpellingDoubled { .. } => 5,
325 Self::MemberShadowsBinding { .. } => 6,
326 Self::AssemblyRoadAbsent => 7,
327 Self::AssemblyRoadNotAnIdentifier { .. } => 8,
328 Self::RefusalSpellingNotAnIdentifier { .. } => 9,
329 Self::ModuleSpellingNotAnIdentifier { .. } => 10,
330 Self::MembersAbsent => 11,
331 Self::MembersUnbounded { .. } => 12,
332 }
333 }
334
335 #[must_use]
337 pub const fn observed(&self) -> Observed {
338 match self {
339 Self::PathSegmentsAbsent
340 | Self::MemberSpellingAbsent
341 | Self::AssemblyRoadAbsent
342 | Self::MembersAbsent => Observed::SeatAbsent,
343 Self::PathSegmentsUnbounded { .. } | Self::MembersUnbounded { .. } => {
344 Observed::BoundExceeded
345 }
346 Self::SegmentNotAnIdentifier { .. }
347 | Self::MemberSpellingNotAnIdentifier { .. }
348 | Self::MemberSpellingDoubled { .. }
349 | Self::MemberShadowsBinding { .. }
350 | Self::AssemblyRoadNotAnIdentifier { .. }
351 | Self::RefusalSpellingNotAnIdentifier { .. }
352 | Self::ModuleSpellingNotAnIdentifier { .. } => Observed::ContractDisagreement,
353 }
354 }
355
356 #[must_use]
358 pub const fn class(&self) -> RefusalClass {
359 match self {
360 Self::PathSegmentsUnbounded { .. } | Self::MembersUnbounded { .. } => {
361 RefusalClass::MagnitudeNotHeld
362 }
363 Self::PathSegmentsAbsent
364 | Self::SegmentNotAnIdentifier { .. }
365 | Self::MemberSpellingAbsent
366 | Self::MemberSpellingNotAnIdentifier { .. }
367 | Self::MemberSpellingDoubled { .. }
368 | Self::MemberShadowsBinding { .. }
369 | Self::AssemblyRoadAbsent
370 | Self::AssemblyRoadNotAnIdentifier { .. }
371 | Self::RefusalSpellingNotAnIdentifier { .. }
372 | Self::ModuleSpellingNotAnIdentifier { .. }
373 | Self::MembersAbsent => RefusalClass::DeclarationNotRead,
374 }
375 }
376}
377
378impl fmt::Display for CodecIssue {
379 fn fmt(&self, into: &mut fmt::Formatter<'_>) -> fmt::Result {
380 match self {
381 Self::PathSegmentsAbsent => into.write_str("a rendered type path names no segment"),
382 Self::SegmentNotAnIdentifier { segment } => {
383 write!(into, "the path segment {segment} is not one Rust identifier")
384 }
385 Self::PathSegmentsUnbounded { bound, observed } => write!(
386 into,
387 "a rendered type path names {observed} segments where {bound} are declared"
388 ),
389 Self::MemberSpellingAbsent => into.write_str("a codec member states no spelling"),
390 Self::MemberSpellingNotAnIdentifier { spelling } => {
391 write!(into, "the member spelling {spelling} is not one Rust identifier")
392 }
393 Self::MemberSpellingDoubled { spelling } => write!(
394 into,
395 "two members of one shape are both spelled {spelling}, so the decode road would bind one local twice"
396 ),
397 Self::MemberShadowsBinding { spelling, binding } => write!(
398 into,
399 "the member {spelling} is spelled like {binding}, which the decode road binds for itself"
400 ),
401 Self::AssemblyRoadAbsent => into.write_str("a codec assembly road states no spelling"),
402 Self::AssemblyRoadNotAnIdentifier { spelling } => {
403 write!(into, "the assembly road {spelling} is not one Rust identifier")
404 }
405 Self::RefusalSpellingNotAnIdentifier { spelling } => write!(
406 into,
407 "the rendered decode refusal {spelling} is not one Rust identifier"
408 ),
409 Self::ModuleSpellingNotAnIdentifier { spelling } => write!(
410 into,
411 "the published module {spelling} is not one Rust identifier"
412 ),
413 Self::MembersAbsent => into.write_str(
414 "a codec shape declares no member, so its decode road could refuse for one reason and admit every other input",
415 ),
416 Self::MembersUnbounded { bound, observed } => write!(
417 into,
418 "a codec shape declares {observed} members where {bound} are declared"
419 ),
420 }
421 }
422}
423
424impl fmt::Display for CodecError {
425 fn fmt(&self, into: &mut fmt::Formatter<'_>) -> fmt::Result {
426 write!(into, "{}", self.first_issue())?;
427 let further = self.issues().count().saturating_sub(1);
428 if further > 0 {
429 write!(into, ", and {further} further issues")?;
430 }
431 if let Capping::Truncated { omitted } = self.capping() {
432 write!(into, ", {omitted} of them not carried")?;
433 }
434 Ok(())
435 }
436}
437
438impl core::error::Error for CodecError {}
439
440impl Refused for CodecError {
441 const PHASE: Phase = Phase::Capture;
442 const FAMILY: Family = CODEC_DECLARATION_FAMILY;
443
444 fn class(&self) -> RefusalClass {
445 self.first_issue().class()
446 }
447
448 fn first(&self) -> String {
449 self.first_issue().to_string()
450 }
451
452 fn observed(&self) -> Observed {
453 self.first_issue().observed()
454 }
455
456 fn body(&self) -> LineBody {
457 let further = self.issues().count().saturating_sub(1);
458 let capping = self.capping();
459 if further == 0 && capping == Capping::Complete {
460 LineBody::SingleCause
461 } else {
462 LineBody::Body { further, capping }
463 }
464 }
465
466 fn related(&self) -> Vec<Vec<u8>> {
468 self.issues()
469 .iter()
470 .skip(1)
471 .map(CodecIssue::canonical_bytes)
472 .collect()
473 }
474
475 fn repairs(&self) -> Bounded<Repair, REPAIR_LIMIT> {
479 Bounded::empty()
480 }
481}