use super::{
CompiledDisagreement, CompiledObservation, CompiledVerdict, DeclaredBehavior, DeclaredReadBack,
ObservedMember,
};
pub fn compared(
observed: &CompiledObservation,
declared: &DeclaredBehavior<'_>,
) -> CompiledVerdict {
match (observed, declared) {
(CompiledObservation::RefusedByCompiler, DeclaredBehavior::RefusedByCompiler) => {
CompiledVerdict::Conforms
}
(CompiledObservation::RefusedByCompiler, DeclaredBehavior::ReadsBack(_)) => {
CompiledVerdict::Deviates(CompiledDisagreement::RefusedWhereAcceptanceDeclared)
}
(CompiledObservation::ReadBack(_), DeclaredBehavior::RefusedByCompiler) => {
CompiledVerdict::Deviates(CompiledDisagreement::AcceptedWhereRefusalDeclared)
}
(CompiledObservation::ReadBack(read), DeclaredBehavior::ReadsBack(expected)) => {
match member_disagreement(read, expected.members()) {
Some(found) => CompiledVerdict::Deviates(found),
None => CompiledVerdict::Conforms,
}
}
}
}
fn member_disagreement(
read: &[ObservedMember],
declared: &[DeclaredReadBack<'_>],
) -> Option<CompiledDisagreement> {
let undeclared = read
.iter()
.find(|member| !declared.iter().any(|expected| expected.name == member.name));
if let Some(member) = undeclared {
return Some(CompiledDisagreement::UnexpectedMember {
member: member.name.clone(),
});
}
if let Some(member) = restated_member(read) {
return Some(CompiledDisagreement::DuplicateMember { member });
}
declared
.iter()
.find_map(|expected| member_value_disagreement(read, expected))
}
fn restated_member(read: &[ObservedMember]) -> Option<String> {
for (position, member) in read.iter().enumerate() {
let restated = read
.iter()
.take(position)
.any(|earlier| earlier.name == member.name);
if restated {
return Some(member.name.clone());
}
}
None
}
fn member_value_disagreement(
read: &[ObservedMember],
expected: &DeclaredReadBack<'_>,
) -> Option<CompiledDisagreement> {
let Some(member) = read.iter().find(|member| member.name == expected.name) else {
return Some(CompiledDisagreement::MissingMember {
member: expected.name.to_owned(),
});
};
if member.value == expected.value {
return None;
}
Some(CompiledDisagreement::MemberValue {
member: expected.name.to_owned(),
})
}