use super::super::wrap::mutant_scoped;
use super::{
CandidateSketch, CheckGap, ClaimPinnedGround, ClaimPinnedProposal, Demonstration,
DischargeAdmissionReceipt, DischargeEvidence, DischargeProposalRefusal, FailureComparison,
HumanAdmissionRefusal, InferredObligation, KillProposalRefusal, MutantKilledGround,
MutantKilledProposal, ObligationComparison, ObligationDischargedGround,
ObligationDischargedProposal, ObligationLane, OwedClaim, OwedDeclaration, ProofDelta,
ProofDeltaRefusal, ProofRefusal, ProposalDestination, ProposalDocument, ProposalRefusal,
ReplayAdmissionReceipt, ReplayBearingProposal, StoredProposalRef, SurvivorExplanation,
SynthesisRefusal,
};
use crate::depot::capsules::{ReplayCapsuleEntry, ReplayDepotSink};
use crate::descriptor::{
CheckRef, ClaimRef, DischargeAdmission, Origin, ReplayAdmission, Row, StagedTableView,
SynthesisFacts,
};
use crate::muterprater::{IntendedRejection, MutationOutcome, MutationReport, MutationTarget};
use crate::report::{ClaimCoverage, Fingerprint, ReplayCapsule, TrialId};
use crate::runner::{Invocation, SelectionPlan, TrialBinding, TrialTable, run_all, trial_identity};
use std::collections::BTreeSet;
pub fn synthesize(
explanation: &SurvivorExplanation,
sketch: &CandidateSketch,
authored_checks: &BTreeSet<CheckRef>,
) -> Result<Row, SynthesisRefusal> {
let closing = explanation.closing();
if !authored_checks.contains(&closing) {
return Err(SynthesisRefusal::CheckGapFound(CheckGap::found(
explanation.claim(),
closing,
explanation.missing(),
)));
}
let Some(point) = explanation.target().identity().point() else {
return Err(SynthesisRefusal::ExternalSurvivorNamesNoPoint);
};
Row::declared(
explanation.claim(),
sketch.suite(),
sketch.classification().clone(),
sketch.subject(),
closing,
sketch.population(),
Origin::Candidate(SynthesisFacts::Survivor(point)),
)
.map_err(SynthesisRefusal::RowRefused)
}
pub fn prove_candidate(
parent: &TrialTable,
candidate: TrialBinding,
target: &MutationTarget,
invocation: &Invocation,
) -> Result<Demonstration, ProofRefusal> {
let trial = trial_identity(candidate.row());
let staged =
StagedTableView::staged(parent, vec![candidate]).map_err(ProofRefusal::StagingRefused)?;
let selection = SelectionPlan::of(mutant_scoped(target));
let report = run_all(&staged.view(), &selection, invocation);
Demonstration::read(report, trial)
}
pub fn offer_mutant_kill(
candidate: Row,
mutation: &MutationReport,
capsule: ReplayCapsule,
demonstration: Demonstration,
known: Vec<Fingerprint>,
destination: ProposalDestination,
) -> Result<MutantKilledProposal, KillProposalRefusal> {
let mutation_rejection = match mutation.outcome() {
MutationOutcome::Killed(IntendedRejection::Demonstrated(rejection)) => rejection,
MutationOutcome::Killed(IntendedRejection::ReportedByBackend { stated: _ })
| MutationOutcome::Survived
| MutationOutcome::Inconclusive(_) => {
return Err(KillProposalRefusal::MutationNotDemonstrated {
verdict: mutation.verdict(),
});
}
};
let fingerprint = demonstration.rejection().fingerprint();
let mutation_fingerprint = mutation_rejection.fingerprint();
if mutation_fingerprint != fingerprint {
return Err(KillProposalRefusal::DemonstrationMismatch {
mutation: mutation_fingerprint.address(),
demonstration: fingerprint.address(),
});
}
let replay_execution = capsule.key().address();
let demonstration_execution = demonstration.trial_report().standing().key().address();
if replay_execution != demonstration_execution {
return Err(KillProposalRefusal::ReplayExecutionMismatch {
replay: replay_execution,
demonstration: demonstration_execution,
});
}
if capsule.fingerprint() != fingerprint {
return Err(KillProposalRefusal::ReplayFingerprintMismatch {
replay: capsule.fingerprint().address(),
demonstration: fingerprint.address(),
});
}
let duplicate =
FailureComparison::compared(fingerprint, known).map_err(KillProposalRefusal::Duplicate)?;
let ground = MutantKilledGround::shown(
mutation.target().clone(),
mutation.activation(),
capsule,
demonstration,
);
MutantKilledProposal::offered(candidate, ground, duplicate, destination)
.map_err(KillProposalRefusal::Refused)
}
pub fn pin_delta(
before: &ClaimCoverage,
after: &ClaimCoverage,
claim: ClaimRef,
) -> Result<ProofDelta, ProofDeltaRefusal> {
ProofDelta::between(exercised(before, claim), exercised(after, claim))
}
pub fn offer_claim_pin(
candidate: Row,
claim: ClaimRef,
capsule: ReplayCapsule,
delta: ProofDelta,
destination: ProposalDestination,
) -> Result<ClaimPinnedProposal, ProposalRefusal> {
ClaimPinnedProposal::offered(
candidate,
ClaimPinnedGround::moved(claim, capsule, delta),
destination,
)
}
pub fn offer_obligation_discharge(
candidate: Row,
owed: OwedClaim,
discharge: DischargeEvidence,
recorded: &[TrialId],
destination: ProposalDestination,
) -> Result<ObligationDischargedProposal, DischargeProposalRefusal> {
let duplicate = ObligationComparison::compared(owed.claim(), recorded)
.map_err(DischargeProposalRefusal::Duplicate)?;
ObligationDischargedProposal::offered(
candidate,
ObligationDischargedGround::discharged(owed, discharge),
duplicate,
destination,
)
.map_err(DischargeProposalRefusal::Refused)
}
fn exercised(coverage: &ClaimCoverage, claim: ClaimRef) -> usize {
coverage.exercise_or_zero(claim).exercised()
}
#[must_use]
pub fn openings(coverage: &ClaimCoverage, declared: &[OwedDeclaration]) -> Vec<InferredObligation> {
declared
.iter()
.filter_map(|declaration| opening(coverage, *declaration))
.collect()
}
fn opening(coverage: &ClaimCoverage, declaration: OwedDeclaration) -> Option<InferredObligation> {
let owed = declaration.owed();
let counted = coverage.exercise_or_zero(owed.claim());
if counted.exercised() == 0_usize {
return Some(InferredObligation::inferred(
owed,
counted,
declaration.shape(),
));
}
None
}
#[must_use]
pub fn route(obligation: &InferredObligation) -> ObligationLane {
ObligationLane::from(obligation.shape())
}
pub fn human_admit_replay<Document, Sink>(
proposal: &Document,
proposal_custody: StoredProposalRef,
depot: &mut Sink,
) -> Result<ReplayAdmissionReceipt, HumanAdmissionRefusal>
where
Document: ReplayBearingProposal,
Sink: ReplayDepotSink,
{
proposal_custody_agrees(proposal, &proposal_custody)?;
let entry =
ReplayCapsuleEntry::admitted(proposal.identity(), proposal.replay_capsule().clone());
let replay = entry.replay();
let row = admitted_row(
proposal,
Origin::AdmittedReplay(ReplayAdmission::admitted(
proposal.identity(),
proposal.replay_ground(),
proposal.destination().suite(),
replay,
)),
)?;
let replay_custody = depot
.store(&entry)
.map_err(HumanAdmissionRefusal::ReplayDepotRefused)?;
if replay_custody.replay() != replay {
return Err(HumanAdmissionRefusal::ReplayCustodyMismatch {
expected: replay,
found: replay_custody.replay(),
});
}
Ok(ReplayAdmissionReceipt::completed(
row,
entry,
proposal_custody,
replay_custody,
))
}
pub fn human_admit_discharge(
proposal: &ObligationDischargedProposal,
proposal_custody: StoredProposalRef,
) -> Result<DischargeAdmissionReceipt, HumanAdmissionRefusal> {
proposal_custody_agrees(proposal, &proposal_custody)?;
let row = admitted_row(
proposal,
Origin::AdmittedDischarge(DischargeAdmission::admitted(
proposal.identity(),
proposal.destination().suite(),
)),
)?;
Ok(DischargeAdmissionReceipt::completed(row, proposal_custody))
}
fn proposal_custody_agrees(
proposal: &impl ProposalDocument,
custody: &StoredProposalRef,
) -> Result<(), HumanAdmissionRefusal> {
let expected = proposal.identity();
let found = custody.proposal();
if expected == found {
return Ok(());
}
Err(HumanAdmissionRefusal::ProposalCustodyMismatch { expected, found })
}
fn admitted_row(
proposal: &impl ProposalDocument,
origin: Origin,
) -> Result<Row, HumanAdmissionRefusal> {
let candidate = proposal.candidate();
Row::declared(
candidate.claim(),
proposal.destination().suite(),
candidate.classification().clone(),
candidate.subject(),
candidate.check(),
candidate.population(),
origin,
)
.map_err(HumanAdmissionRefusal::RowRefused)
}