use super::{ArchiveMaterial, Archives, Basic, LongTerm, Timed};
use crate::{material, prepare::Preparation, unsigned::Requirements};
type BasicOf<'a, N, D, G> = Basic<'a, 'a, 'a, 'a, N, D, G>;
type TimedOf<'a, N, D, G> = Timed<'a, 'a, 'a, 'a, 'a, 'a, N, D, G>;
type CredentialsOf<'a, A, N, D, G> = material::credentials::Report<'a, 'a, 'a, A, N, D, G>;
type LongTermOf<'a, A, N, D, G> = LongTerm<
'a,
TimedOf<'a, N, D, G>,
material::Checks<'a, 'a, 'a, 'a, 'a, N, D, G>,
CredentialsOf<'a, A, N, D, G>,
>;
type ArchivesOf<'a, A, N, D, G> = Archives<
'a,
Preparation<N, D, G>,
ArchiveMaterial<
'a,
material::ParticipantMaterial<'a, 'a, 'a, 'a, 'a, N, D, G>,
CredentialsOf<'a, A, N, D, G>,
>,
>;
pub enum Evidence<'a, A, N, D, G> {
Basic(&'a BasicOf<'a, N, D, G>),
Timestamped(&'a TimedOf<'a, N, D, G>),
LongTerm(&'a LongTermOf<'a, A, N, D, G>),
Archival {
long_term: &'a LongTermOf<'a, A, N, D, G>,
archives: &'a ArchivesOf<'a, A, N, D, G>,
},
}
impl<'a, N, D, G> Evidence<'a, (), N, D, G> {
#[must_use]
pub fn basic(report: &'a BasicOf<'a, N, D, G>) -> Self {
Self::Basic(report)
}
#[must_use]
pub fn timestamped(report: &'a TimedOf<'a, N, D, G>) -> Self {
Self::Timestamped(report)
}
}
impl<A, N: Copy, D: Copy, G: Copy> Evidence<'_, A, N, D, G> {
#[must_use]
pub fn level(&self) -> Requirements {
match self {
Self::Basic(_) => Requirements::Basic,
Self::Timestamped(_) => Requirements::Timestamped,
Self::LongTerm(_) => Requirements::LongTerm,
Self::Archival { .. } => Requirements::Archival,
}
}
fn prepared(&self) -> &Preparation<N, D, G> {
match self {
Self::Basic(basic) => basic.inputs().fields.prepared(),
Self::Timestamped(timed) => timed.basic().inputs().fields.prepared(),
Self::LongTerm(long_term) | Self::Archival { long_term, .. } => {
long_term.timed().basic().inputs().fields.prepared()
}
}
}
}
pub struct Assessment<'a, A, N, D, G> {
evidence: Evidence<'a, A, N, D, G>,
met: bool,
}
impl<'a, A, N: Copy, D: Copy, G: Copy> Assessment<'a, A, N, D, G> {
#[must_use]
pub fn requested(&self) -> Requirements {
self.evidence.level()
}
#[must_use]
pub fn evidence(&self) -> &Evidence<'a, A, N, D, G> {
&self.evidence
}
#[must_use]
pub fn all_requirements_met(&self) -> bool {
self.met
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LevelError {
Mismatch {
requested: Requirements,
supplied: Requirements,
},
Foreign,
}
pub fn assess<A, N: Copy, D: Copy, G: Copy>(
evidence: Evidence<'_, A, N, D, G>,
) -> Result<Assessment<'_, A, N, D, G>, LevelError> {
let requested = evidence.prepared().requirements();
let supplied = evidence.level();
if requested != supplied {
return Err(LevelError::Mismatch {
requested,
supplied,
});
}
let met = match &evidence {
Evidence::Basic(basic) => basic.all_root_requirements_met(),
Evidence::Timestamped(timed) => timed_met(timed),
Evidence::LongTerm(long_term) => long_term_met(long_term),
Evidence::Archival {
long_term,
archives,
} => {
let census = long_term.credentials().credentials();
if !core::ptr::eq(archives.prepared(), evidence.prepared())
|| archives
.reports()
.iter()
.any(|report| !core::ptr::eq(report.credentials().credentials(), census))
{
return Err(LevelError::Foreign);
}
long_term_met(long_term) && archives.all_archive_material_covered()
}
};
Ok(Assessment { evidence, met })
}
fn timed_met<N: Copy, D: Copy, G: Copy>(timed: &TimedOf<'_, N, D, G>) -> bool {
timed.basic().all_root_requirements_met() && timed.all_static_time_requirements_met()
}
fn long_term_met<A, N: Copy, D: Copy, G: Copy>(long_term: &LongTermOf<'_, A, N, D, G>) -> bool {
timed_met(long_term.timed()) && long_term.all_material_requirements_met()
}