use super::spell::{
CANDIDATE_BINDING, CARRIED_BINDING, CHOSEN_BINDING, COLLECTED_BINDING, ELECTED_BINDING,
INTO_BINDING, LENGTH_BINDING, MATERIAL_BINDING, NESTED_BINDING, PRESENT_BINDING,
REMAINING_BINDING, WIDTH_BINDING,
};
use super::{
AssemblyPosture, CodecContent, CodecDirection, CodecError, CodecIssue, CodecMemberShape,
CodecPlacement, CodecProjection, CodecTypePath, DECODE_ROAD, DecodeRefusal, ENCODE_ROAD,
MemberContract, ROSTER_CONSTANT, SLOT_ROAD,
};
use crate::bounded::{Bounded, Capping};
use crate::diagnostic::{
CODEC_DECLARATION_FAMILY, Family, LineBody, Observed, Phase, REPAIR_LIMIT, RefusalClass,
Refused, Repair,
};
use crate::identity::{OwnerIdentity, encode_bytes, encode_length};
use crate::kind::{CanonicalContent, Kind, NoQuestions, SoleRole};
use core::fmt;
impl CanonicalContent for CodecContent {
fn encode_content_into(&self, into: &mut Vec<u8>) {
encode_path(self.shape.owner(), into);
encode_bytes(self.shape.refusal().as_bytes(), into);
let assembly = self.shape.assembly();
encode_bytes(assembly.road().as_bytes(), into);
match assembly.posture() {
AssemblyPosture::Total => into.push(0),
AssemblyPosture::Checked { refusal } => {
into.push(1);
encode_path(refusal, into);
}
}
encode_length(self.shape.count(), into);
for member in self.shape.members() {
let mut encoded = Vec::new();
encode_bytes(member.spelling().as_bytes(), &mut encoded);
encode_path(member.held_as(), &mut encoded);
encode_bytes(member.shape().name().as_bytes(), &mut encoded);
encode_bytes(member.cardinality().name().as_bytes(), &mut encoded);
encode_bytes(&encoded, into);
}
encode_bytes(self.direction.name().as_bytes(), into);
match &self.placement {
CodecPlacement::AtDeclarationSite => into.push(0),
CodecPlacement::PublishedModule { spelling } => {
into.push(1);
encode_bytes(spelling.spelling().as_bytes(), into);
}
}
encode_owner(self.schema.as_ref(), into);
encode_owner(self.byte_role.as_ref(), into);
encode_length(self.assumptions.len(), into);
for assumption in self.assumptions.as_slice() {
encode_bytes(&assumption.citation_bytes(), into);
}
}
}
fn encode_path(path: &CodecTypePath, into: &mut Vec<u8>) {
encode_bytes(path.rooting().name().as_bytes(), into);
encode_length(path.count(), into);
for segment in path.segments() {
encode_bytes(segment.as_bytes(), into);
}
}
fn encode_owner(owner: Option<&OwnerIdentity>, into: &mut Vec<u8>) {
match owner {
None => into.push(0),
Some(identity) => {
into.push(1);
encode_bytes(&identity.citation_bytes(), into);
}
}
}
impl Kind for CodecProjection {
const NAME: &'static str = "codec-projection";
type Content = CodecContent;
type Role = SoleRole;
type Question = NoQuestions;
}
impl CodecDirection {
#[must_use]
pub const fn writes(self) -> bool {
match self {
Self::Encode | Self::RoundTrip => true,
Self::Decode => false,
}
}
#[must_use]
pub const fn reads(self) -> bool {
match self {
Self::Decode | Self::RoundTrip => true,
Self::Encode => false,
}
}
}
impl DecodeRefusal {
#[must_use]
pub const fn carries_member(self) -> bool {
match self {
Self::Truncated
| Self::LengthPastRemaining
| Self::LengthPastAddressableWidth
| Self::CountPastDeclaredWidth
| Self::TextNotUtf8
| Self::MemberNotAdmitted
| Self::SlotNotAdmitted
| Self::NestedMemberRefused
| Self::PresenceNotAdmitted => true,
Self::TrailingBytes | Self::NotAssembled => false,
}
}
#[must_use]
pub const fn sentence(self) -> &'static str {
match self {
Self::Truncated => "The material ended inside this member.",
Self::LengthPastRemaining => {
"This member's declared length runs past the material that remains."
}
Self::LengthPastAddressableWidth => {
"This member's declared length does not fit an addressable width."
}
Self::CountPastDeclaredWidth => {
"This member's declared count does not fit the width the member is held at."
}
Self::TextNotUtf8 => "This member's framed bytes are not UTF-8.",
Self::MemberNotAdmitted => "The member's own type refused what was read for it.",
Self::SlotNotAdmitted => {
"The slot read for this member names no arm of the roster it was declared over."
}
Self::NestedMemberRefused => {
"The nested codec this member carries refused the framed material."
}
Self::PresenceNotAdmitted => {
"This member's presence byte is neither of the two the encode road writes."
}
Self::TrailingBytes => {
"Material remains after the last declared member. A canonical encoding is the \
whole of what a value writes, so a longer input is not this value with something \
after it."
}
Self::NotAssembled => {
"Every member was read, and the road that assembles them refused. The refusal is \
the owner's own, carried exactly."
}
}
}
}
pub const MEMBER_CONTRACT: [MemberContract; 5] = [
COUNT_CONTRACT.bill,
BYTES_CONTRACT.bill,
TEXT_CONTRACT.bill,
CLOSED_CHOICE_CONTRACT.bill,
NESTED_CONTRACT.bill,
];
#[derive(Clone, Copy)]
pub(super) enum WriteRoad {
Count,
Bytes,
Text,
ClosedChoice,
Nested,
}
#[derive(Clone, Copy)]
pub(super) enum ReadRoad {
Count,
Bytes,
Text,
ClosedChoice,
Nested,
}
#[derive(Clone, Copy)]
pub(super) struct RenderingContract {
pub(super) bill: MemberContract,
pub(super) write: WriteRoad,
pub(super) read: ReadRoad,
}
const COUNT_CONTRACT: RenderingContract = RenderingContract {
bill: MemberContract {
shape: CodecMemberShape::Count,
encode_road: "u64::from",
decode_road: "<T as ::core::convert::TryFrom<u64>>::try_from",
},
write: WriteRoad::Count,
read: ReadRoad::Count,
};
const BYTES_CONTRACT: RenderingContract = RenderingContract {
bill: MemberContract {
shape: CodecMemberShape::Bytes,
encode_road: "<T as ::core::convert::AsRef<[u8]>>::as_ref",
decode_road: "<T as ::core::convert::TryFrom<::std::vec::Vec<u8>>>::try_from",
},
write: WriteRoad::Bytes,
read: ReadRoad::Bytes,
};
const TEXT_CONTRACT: RenderingContract = RenderingContract {
bill: MemberContract {
shape: CodecMemberShape::Text,
encode_road: "<T as ::core::convert::AsRef<str>>::as_ref",
decode_road: "<T as ::core::convert::TryFrom<::std::string::String>>::try_from",
},
write: WriteRoad::Text,
read: ReadRoad::Text,
};
const CLOSED_CHOICE_CONTRACT: RenderingContract = RenderingContract {
bill: MemberContract {
shape: CodecMemberShape::ClosedChoice,
encode_road: SLOT_ROAD,
decode_road: ROSTER_CONSTANT,
},
write: WriteRoad::ClosedChoice,
read: ReadRoad::ClosedChoice,
};
const NESTED_CONTRACT: RenderingContract = RenderingContract {
bill: MemberContract {
shape: CodecMemberShape::Nested,
encode_road: ENCODE_ROAD,
decode_road: DECODE_ROAD,
},
write: WriteRoad::Nested,
read: ReadRoad::Nested,
};
pub(super) const fn rendering_contract(shape: CodecMemberShape) -> RenderingContract {
match shape {
CodecMemberShape::Count => COUNT_CONTRACT,
CodecMemberShape::Bytes => BYTES_CONTRACT,
CodecMemberShape::Text => TEXT_CONTRACT,
CodecMemberShape::ClosedChoice => CLOSED_CHOICE_CONTRACT,
CodecMemberShape::Nested => NESTED_CONTRACT,
}
}
pub const RESERVED_BINDINGS: [&str; 12] = [
MATERIAL_BINDING,
REMAINING_BINDING,
INTO_BINDING,
NESTED_BINDING,
COLLECTED_BINDING,
CANDIDATE_BINDING,
CHOSEN_BINDING,
ELECTED_BINDING,
PRESENT_BINDING,
CARRIED_BINDING,
LENGTH_BINDING,
WIDTH_BINDING,
];
impl CodecIssue {
#[must_use]
pub const fn slot(&self) -> u8 {
match self {
Self::PathSegmentsAbsent => 0,
Self::SegmentNotAnIdentifier { .. } => 1,
Self::PathSegmentsUnbounded { .. } => 2,
Self::MemberSpellingAbsent => 3,
Self::MemberSpellingNotAnIdentifier { .. } => 4,
Self::MemberSpellingDoubled { .. } => 5,
Self::MemberShadowsBinding { .. } => 6,
Self::AssemblyRoadAbsent => 7,
Self::AssemblyRoadNotAnIdentifier { .. } => 8,
Self::RefusalSpellingNotAnIdentifier { .. } => 9,
Self::ModuleSpellingNotAnIdentifier { .. } => 10,
Self::MembersAbsent => 11,
Self::MembersUnbounded { .. } => 12,
}
}
#[must_use]
pub const fn observed(&self) -> Observed {
match self {
Self::PathSegmentsAbsent
| Self::MemberSpellingAbsent
| Self::AssemblyRoadAbsent
| Self::MembersAbsent => Observed::SeatAbsent,
Self::PathSegmentsUnbounded { .. } | Self::MembersUnbounded { .. } => {
Observed::BoundExceeded
}
Self::SegmentNotAnIdentifier { .. }
| Self::MemberSpellingNotAnIdentifier { .. }
| Self::MemberSpellingDoubled { .. }
| Self::MemberShadowsBinding { .. }
| Self::AssemblyRoadNotAnIdentifier { .. }
| Self::RefusalSpellingNotAnIdentifier { .. }
| Self::ModuleSpellingNotAnIdentifier { .. } => Observed::ContractDisagreement,
}
}
#[must_use]
pub const fn class(&self) -> RefusalClass {
match self {
Self::PathSegmentsUnbounded { .. } | Self::MembersUnbounded { .. } => {
RefusalClass::MagnitudeNotHeld
}
Self::PathSegmentsAbsent
| Self::SegmentNotAnIdentifier { .. }
| Self::MemberSpellingAbsent
| Self::MemberSpellingNotAnIdentifier { .. }
| Self::MemberSpellingDoubled { .. }
| Self::MemberShadowsBinding { .. }
| Self::AssemblyRoadAbsent
| Self::AssemblyRoadNotAnIdentifier { .. }
| Self::RefusalSpellingNotAnIdentifier { .. }
| Self::ModuleSpellingNotAnIdentifier { .. }
| Self::MembersAbsent => RefusalClass::DeclarationNotRead,
}
}
}
impl fmt::Display for CodecIssue {
fn fmt(&self, into: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::PathSegmentsAbsent => into.write_str("a rendered type path names no segment"),
Self::SegmentNotAnIdentifier { segment } => {
write!(into, "the path segment {segment} is not one Rust identifier")
}
Self::PathSegmentsUnbounded { bound, observed } => write!(
into,
"a rendered type path names {observed} segments where {bound} are declared"
),
Self::MemberSpellingAbsent => into.write_str("a codec member states no spelling"),
Self::MemberSpellingNotAnIdentifier { spelling } => {
write!(into, "the member spelling {spelling} is not one Rust identifier")
}
Self::MemberSpellingDoubled { spelling } => write!(
into,
"two members of one shape are both spelled {spelling}, so the decode road would bind one local twice"
),
Self::MemberShadowsBinding { spelling, binding } => write!(
into,
"the member {spelling} is spelled like {binding}, which the decode road binds for itself"
),
Self::AssemblyRoadAbsent => into.write_str("a codec assembly road states no spelling"),
Self::AssemblyRoadNotAnIdentifier { spelling } => {
write!(into, "the assembly road {spelling} is not one Rust identifier")
}
Self::RefusalSpellingNotAnIdentifier { spelling } => write!(
into,
"the rendered decode refusal {spelling} is not one Rust identifier"
),
Self::ModuleSpellingNotAnIdentifier { spelling } => write!(
into,
"the published module {spelling} is not one Rust identifier"
),
Self::MembersAbsent => into.write_str(
"a codec shape declares no member, so its decode road could refuse for one reason and admit every other input",
),
Self::MembersUnbounded { bound, observed } => write!(
into,
"a codec shape declares {observed} members where {bound} are declared"
),
}
}
}
impl fmt::Display for CodecError {
fn fmt(&self, into: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(into, "{}", self.first_issue())?;
let further = self.issues().count().saturating_sub(1);
if further > 0 {
write!(into, ", and {further} further issues")?;
}
if let Capping::Truncated { omitted } = self.capping() {
write!(into, ", {omitted} of them not carried")?;
}
Ok(())
}
}
impl core::error::Error for CodecError {}
impl Refused for CodecError {
const PHASE: Phase = Phase::Capture;
const FAMILY: Family = CODEC_DECLARATION_FAMILY;
fn class(&self) -> RefusalClass {
self.first_issue().class()
}
fn first(&self) -> String {
self.first_issue().to_string()
}
fn observed(&self) -> Observed {
self.first_issue().observed()
}
fn body(&self) -> LineBody {
let further = self.issues().count().saturating_sub(1);
let capping = self.capping();
if further == 0 && capping == Capping::Complete {
LineBody::SingleCause
} else {
LineBody::Body { further, capping }
}
}
fn related(&self) -> Vec<Vec<u8>> {
self.issues()
.iter()
.skip(1)
.map(CodecIssue::canonical_bytes)
.collect()
}
fn repairs(&self) -> Bounded<Repair, REPAIR_LIMIT> {
Bounded::empty()
}
}