use super::types::{
AdmissionGround, CheckRef, ClaimRef, Classification, DESCRIPTOR_PROJECTIONS,
DescriptorProjection, DischargeAdmission, EncodeRefusal, ExecutionSuite, FieldCardinality,
FieldShape, GeneratedSupportSchema, NamespacedName, Origin, PopulationRef, ReplayAdmission,
SchemaField, SubjectRoute, SynthesisFacts, TrialCoordinates, generated_support_members,
origin_declarations,
};
use crate::identity::{encode_bytes, encode_length};
const SCHEMA_ENCODING_VERSION: u32 = 1;
const ROW_ENCODING_VERSION: u32 = 2;
impl AdmissionGround {
#[must_use]
pub const fn slot(self) -> u8 {
match self {
Self::MutantKilled => 1,
Self::ClaimPinned => 2,
Self::ObligationDischarged => 3,
}
}
}
macro_rules! implement_origin_slots {
($( $variant:ident $(($payload:pat))? => $spelling:literal => $slot:literal, )+) => {
impl Origin {
#[must_use]
pub const fn slot(self) -> u8 {
match self {
$(
Self::$variant $(($payload))? => $slot,
)+
}
}
}
};
}
origin_declarations!(implement_origin_slots);
impl SynthesisFacts {
#[must_use]
pub const fn slot(self) -> u8 {
match self {
Self::Survivor(_) => 1,
Self::ProofGap => 2,
}
}
}
impl FieldShape {
#[must_use]
pub const fn slot(self) -> u8 {
match self {
Self::NamespacedName => 1,
Self::ContentAddress => 2,
Self::ClosedChoice(_) => 3,
Self::Bytes => 4,
Self::Count => 5,
Self::MutationAlternative => 6,
}
}
}
impl FieldCardinality {
#[must_use]
pub const fn slot(self) -> u8 {
match self {
Self::ExactlyOne => 1,
Self::ZeroOrOne => 2,
Self::ZeroOrMore => 3,
Self::OneOrMore => 4,
}
}
}
macro_rules! push_generated_support_members {
([$bytes:ident, $schema:ident]; $( $member:ident: $member_type:ty => $fields:ident => $tag:literal, )+) => {
$(
push_member(&mut $bytes, $tag, $schema.$member().fields())?;
)+
};
}
pub fn encode_generated_support_schema(
schema: &GeneratedSupportSchema,
) -> Result<Vec<u8>, EncodeRefusal> {
let mut bytes = Vec::new();
bytes.extend_from_slice(&SCHEMA_ENCODING_VERSION.to_be_bytes());
generated_support_members!(push_generated_support_members, bytes, schema);
Ok(bytes)
}
fn push_member(out: &mut Vec<u8>, tag: u8, fields: &[SchemaField]) -> Result<(), EncodeRefusal> {
out.push(tag);
encode_declared_length(fields.len(), out)?;
for field in fields {
encode_declared_bytes(field.name().as_bytes(), out)?;
push_shape(out, field.shape())?;
out.push(field.cardinality().slot());
}
Ok(())
}
fn push_shape(out: &mut Vec<u8>, shape: FieldShape) -> Result<(), EncodeRefusal> {
out.push(shape.slot());
match shape {
FieldShape::ClosedChoice(arms) => {
encode_declared_length(arms.len(), out)?;
for arm in arms {
encode_declared_bytes(arm.as_bytes(), out)?;
}
}
FieldShape::NamespacedName
| FieldShape::ContentAddress
| FieldShape::Bytes
| FieldShape::Count
| FieldShape::MutationAlternative => {}
}
Ok(())
}
pub(super) fn encode_row_content(
claim: ClaimRef,
execution_suite: ExecutionSuite,
classification: &Classification,
subject: SubjectRoute,
check: CheckRef,
population: PopulationRef,
origin: Origin,
) -> Result<Vec<u8>, EncodeRefusal> {
RowEncoding::over(
claim,
execution_suite,
classification,
subject,
check,
population,
origin,
)
.encoded()
}
struct RowEncoding<'classification> {
bytes: Vec<u8>,
claim: ClaimRef,
execution_suite: ExecutionSuite,
classification: &'classification Classification,
subject: SubjectRoute,
check: CheckRef,
population: PopulationRef,
origin: Origin,
}
impl<'classification> RowEncoding<'classification> {
fn over(
claim: ClaimRef,
execution_suite: ExecutionSuite,
classification: &'classification Classification,
subject: SubjectRoute,
check: CheckRef,
population: PopulationRef,
origin: Origin,
) -> Self {
Self {
bytes: ROW_ENCODING_VERSION.to_be_bytes().to_vec(),
claim,
execution_suite,
classification,
subject,
check,
population,
origin,
}
}
fn encoded(mut self) -> Result<Vec<u8>, EncodeRefusal> {
for projection in DESCRIPTOR_PROJECTIONS {
self.push_projection(*projection)?;
}
Ok(self.bytes)
}
fn push_projection(&mut self, projection: DescriptorProjection) -> Result<(), EncodeRefusal> {
match projection {
DescriptorProjection::Claim => self.claim.name().encode_into(&mut self.bytes),
DescriptorProjection::ExecutionSuite => {
self.execution_suite.name().encode_into(&mut self.bytes);
}
DescriptorProjection::Roles => self.push_roles()?,
DescriptorProjection::Tags => self.push_tags()?,
DescriptorProjection::Subject => self.subject.name().encode_into(&mut self.bytes),
DescriptorProjection::Check => self.check.name().encode_into(&mut self.bytes),
DescriptorProjection::Population => {
self.population.name().encode_into(&mut self.bytes);
}
DescriptorProjection::Origin => push_origin(&mut self.bytes, self.origin)?,
}
Ok(())
}
fn push_roles(&mut self) -> Result<(), EncodeRefusal> {
encode_declared_length(self.classification.roles().len(), &mut self.bytes)?;
for role in self.classification.roles() {
role.name().encode_into(&mut self.bytes);
}
Ok(())
}
fn push_tags(&mut self) -> Result<(), EncodeRefusal> {
encode_declared_length(self.classification.tags().len(), &mut self.bytes)?;
for tag in self.classification.tags() {
tag.name().encode_into(&mut self.bytes);
}
Ok(())
}
}
pub(super) fn encode_trial_coordinates(coordinates: TrialCoordinates) -> Vec<u8> {
let mut out = Vec::new();
coordinates.claim().name().encode_into(&mut out);
coordinates.subject().name().encode_into(&mut out);
coordinates.check().name().encode_into(&mut out);
coordinates.population().name().encode_into(&mut out);
out
}
fn push_origin(out: &mut Vec<u8>, origin: Origin) -> Result<(), EncodeRefusal> {
out.push(origin.slot());
match origin {
Origin::HandWritten => Ok(()),
Origin::Generated(facts) => {
facts.door().name().encode_into(out);
facts.projection().name().encode_into(out);
Ok(())
}
Origin::Candidate(facts) => {
push_synthesis(out, facts);
Ok(())
}
Origin::AdmittedReplay(admitted) => push_replay_admission(out, admitted),
Origin::AdmittedDischarge(admitted) => push_discharge_admission(out, admitted),
}
}
fn push_synthesis(out: &mut Vec<u8>, facts: SynthesisFacts) {
out.push(facts.slot());
match facts {
SynthesisFacts::Survivor(point) => point.name().encode_into(out),
SynthesisFacts::ProofGap => {}
}
}
fn push_replay_admission(
out: &mut Vec<u8>,
admitted: ReplayAdmission,
) -> Result<(), EncodeRefusal> {
encode_declared_bytes(admitted.proposal().address().as_bytes(), out)?;
out.push(admitted.admission().ground().slot());
admitted.destination().name().encode_into(out);
encode_declared_bytes(admitted.replay().address().as_bytes(), out)
}
fn push_discharge_admission(
out: &mut Vec<u8>,
admitted: DischargeAdmission,
) -> Result<(), EncodeRefusal> {
encode_declared_bytes(admitted.proposal().address().as_bytes(), out)?;
admitted.destination().name().encode_into(out);
Ok(())
}
impl NamespacedName {
pub fn encode_into(self, into: &mut Vec<u8>) {
encode_bytes(self.namespace().written().as_bytes(), into);
encode_bytes(self.stem().written().as_bytes(), into);
}
}
pub(crate) fn encode_declared_length(
length: usize,
into: &mut Vec<u8>,
) -> Result<(), EncodeRefusal> {
declared_length(length)?;
encode_length(length, into);
Ok(())
}
pub(crate) fn encode_declared_bytes(
material: &[u8],
into: &mut Vec<u8>,
) -> Result<(), EncodeRefusal> {
declared_length(material.len())?;
encode_bytes(material, into);
Ok(())
}
fn declared_length(length: usize) -> Result<(), EncodeRefusal> {
u64::try_from(length).map_err(|_| EncodeRefusal::LengthPastEncodingWidth)?;
Ok(())
}