use super::artifact::{parse_header, CandidateHeader, Coverage};
use super::context_summary;
use super::expectations::AdmissionExpectations;
use super::reasons::RefusalReason;
#[derive(Debug, Clone)]
pub struct AdmittedFacts {
pub header: CandidateHeader,
pub bytes: Vec<u8>,
}
fn inventory_is_coherent(header: &CandidateHeader) -> bool {
!header
.units_processed
.iter()
.any(|unit| header.units_missing.contains(unit))
}
fn context_summary_matches(header: &CandidateHeader) -> bool {
match (
&header.context_fingerprint_header,
&header.context_compilations,
) {
(Some(header_fp), Some(compilations)) => {
*header_fp == context_summary::recompute(compilations)
}
_ => false,
}
}
fn occurrence_self_consistency(header: &CandidateHeader) -> Option<RefusalReason> {
if header.occurrence_sites.is_some() != header.occurrences_capability_provided {
return Some(RefusalReason::OccurrenceCapabilityMismatch);
}
let sites = header.occurrence_sites.as_ref()?;
let compilations = header.context_compilations.as_ref()?;
for site in sites {
match compilations.iter().find(|c| c.identity == site.identity) {
None => return Some(RefusalReason::OccurrenceCompilationUnknown),
Some(matched) if matched.fingerprint.is_none() => {
return Some(RefusalReason::OccurrenceCompilationUnsupported)
}
Some(_) => {}
}
}
None
}
fn identity_check(
header: &CandidateHeader,
expected: &AdmissionExpectations,
) -> Option<RefusalReason> {
if header.contract_version != expected.contract_version {
return Some(RefusalReason::ContractVersionMismatch);
}
if header.producer_name != expected.producer_name {
return Some(RefusalReason::ProducerMismatch);
}
if header.engine_revision != expected.engine_revision {
return Some(RefusalReason::EngineRevisionMismatch);
}
if header.profile != expected.profile {
return Some(RefusalReason::ProfileMismatch);
}
if header.dependency_fingerprint != expected.dependency_fingerprint {
return Some(RefusalReason::DependencyFingerprintMismatch);
}
if header.context_schema_version != expected.context_schema_version {
return Some(RefusalReason::ContextEnvelopeVersionUnrecognised);
}
if !context_summary_matches(header) {
return Some(RefusalReason::ContextFingerprintMismatch);
}
if let Some(expected_head) = &expected.head_sha {
if header.source_head_sha.as_deref() != Some(expected_head.as_str()) {
return Some(RefusalReason::SourceSnapshotMismatch);
}
}
None
}
pub fn admit(
candidate_bytes: &[u8],
expected: &AdmissionExpectations,
) -> Result<AdmittedFacts, RefusalReason> {
let header = parse_header(candidate_bytes)?;
if !header.completion_terminal {
return Err(RefusalReason::MissingCompletionRecord);
}
if !inventory_is_coherent(&header) {
return Err(RefusalReason::IncoherentInventory);
}
if let Some(reason) = occurrence_self_consistency(&header) {
return Err(reason);
}
if let Some(reason) = identity_check(&header, expected) {
return Err(reason);
}
Ok(AdmittedFacts {
header,
bytes: candidate_bytes.to_vec(),
})
}
impl AdmittedFacts {
pub fn coverage(&self) -> &Coverage {
&self.header.coverage
}
}