use crate::descriptor::{
AdmissionGround, ExecutionSuite, Namespace, Origin, ProposalId, ReplayBearingGround, Row,
SynthesisFacts,
};
use crate::identity::ContentAddress;
use crate::muterprater::MutationTarget;
use crate::muterprater::proposal::types::{
ClaimPinnedGround, ClaimPinnedProposal, FailureComparison, MutantKilledGround,
MutantKilledProposal, NoComparison, NoComparisonReason, ObligationComparison,
ObligationDischargedGround, ObligationDischargedProposal, PROPOSAL_TAG, ProposalDestination,
ProposalDocument, ProposalRefusal, ReplayBearingProposal,
};
use crate::report::{ReplayCapsule, encode_bytes};
const PROPOSAL_ENCODING_VERSION: u32 = 1;
impl ProposalDestination {
#[must_use]
pub const fn naming(suite: ExecutionSuite) -> Self {
Self { suite }
}
#[must_use]
pub const fn suite(self) -> ExecutionSuite {
self.suite
}
#[must_use]
pub const fn owner(self) -> Namespace {
self.suite.name().namespace()
}
}
impl MutantKilledProposal {
pub(in crate::muterprater) fn offered(
candidate: Row,
ground: MutantKilledGround,
duplicate: FailureComparison,
destination: ProposalDestination,
) -> Result<Self, ProposalRefusal> {
let facts = candidate_facts(&candidate)?;
survivor_point_agrees(facts, ground.target())?;
Ok(Self {
candidate,
ground,
duplicate,
destination,
})
}
#[must_use]
pub const fn ground(&self) -> &MutantKilledGround {
&self.ground
}
#[must_use]
pub const fn duplicate(&self) -> &FailureComparison {
&self.duplicate
}
}
impl ClaimPinnedProposal {
pub(in crate::muterprater) fn offered(
candidate: Row,
ground: ClaimPinnedGround,
destination: ProposalDestination,
) -> Result<Self, ProposalRefusal> {
candidate_facts(&candidate)?;
Ok(Self {
candidate,
ground,
duplicate: NoComparison::stated(NoComparisonReason::GroundCarriesNoFailure),
destination,
})
}
#[must_use]
pub const fn ground(&self) -> &ClaimPinnedGround {
&self.ground
}
#[must_use]
pub const fn duplicate(&self) -> NoComparison {
self.duplicate
}
}
impl ObligationDischargedProposal {
pub(in crate::muterprater) fn offered(
candidate: Row,
ground: ObligationDischargedGround,
duplicate: ObligationComparison,
destination: ProposalDestination,
) -> Result<Self, ProposalRefusal> {
candidate_facts(&candidate)?;
Ok(Self {
candidate,
ground,
duplicate,
destination,
})
}
#[must_use]
pub const fn ground(&self) -> &ObligationDischargedGround {
&self.ground
}
#[must_use]
pub const fn duplicate(&self) -> &ObligationComparison {
&self.duplicate
}
}
impl ProposalDocument for MutantKilledProposal {
fn candidate(&self) -> &Row {
&self.candidate
}
fn ground_summary(&self) -> AdmissionGround {
AdmissionGround::from(&self.ground)
}
fn destination(&self) -> ProposalDestination {
self.destination
}
fn identity(&self) -> ProposalId {
proposal_identity(&self.candidate, self.ground_summary(), self.destination)
}
}
impl ProposalDocument for ClaimPinnedProposal {
fn candidate(&self) -> &Row {
&self.candidate
}
fn ground_summary(&self) -> AdmissionGround {
AdmissionGround::from(&self.ground)
}
fn destination(&self) -> ProposalDestination {
self.destination
}
fn identity(&self) -> ProposalId {
proposal_identity(&self.candidate, self.ground_summary(), self.destination)
}
}
impl ProposalDocument for ObligationDischargedProposal {
fn candidate(&self) -> &Row {
&self.candidate
}
fn ground_summary(&self) -> AdmissionGround {
AdmissionGround::from(&self.ground)
}
fn destination(&self) -> ProposalDestination {
self.destination
}
fn identity(&self) -> ProposalId {
proposal_identity(&self.candidate, self.ground_summary(), self.destination)
}
}
impl ReplayBearingProposal for MutantKilledProposal {
fn replay_capsule(&self) -> &ReplayCapsule {
self.ground.capsule()
}
fn replay_ground(&self) -> ReplayBearingGround {
ReplayBearingGround::MutantKilled
}
}
impl ReplayBearingProposal for ClaimPinnedProposal {
fn replay_capsule(&self) -> &ReplayCapsule {
self.ground.capsule()
}
fn replay_ground(&self) -> ReplayBearingGround {
ReplayBearingGround::ClaimPinned
}
}
fn proposal_identity(
candidate: &Row,
ground: AdmissionGround,
destination: ProposalDestination,
) -> ProposalId {
let mut preimage = Vec::new();
preimage.extend_from_slice(&PROPOSAL_ENCODING_VERSION.to_be_bytes());
encode_bytes(candidate.canonical_bytes().as_bytes(), &mut preimage);
preimage.push(ground.slot());
destination.suite().name().encode_into(&mut preimage);
ProposalId::over(ContentAddress::derived(PROPOSAL_TAG, &preimage))
}
fn candidate_facts(candidate: &Row) -> Result<SynthesisFacts, ProposalRefusal> {
match candidate.origin() {
Origin::Candidate(facts) => Ok(facts),
Origin::HandWritten
| Origin::Generated(_)
| Origin::AdmittedReplay(_)
| Origin::AdmittedDischarge(_) => Err(ProposalRefusal::NotACandidate),
}
}
fn survivor_point_agrees(
facts: SynthesisFacts,
target: &MutationTarget,
) -> Result<(), ProposalRefusal> {
let SynthesisFacts::Survivor(synthesis) = facts else {
return Ok(());
};
let Some(point) = target.identity().point() else {
return Ok(());
};
if synthesis == point {
return Ok(());
}
Err(ProposalRefusal::SurvivorPointMismatch {
synthesis,
target: point,
})
}