use super::{Error, Graph, OwnedVec, RangeSource, ResultOf, Work, resource};
use crate::{references::DigestReading, timestamp};
use structured_xades::certificate::Agreement;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimestampState {
Unchecked,
Failed,
Proven,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ObjectsState {
Unchecked,
Failed,
Matches,
}
pub struct TimestampCheck<'r, 'd, D, G> {
node: usize,
receipt: Option<&'r timestamp::Checked<'d, D, G>>,
timestamp: TimestampState,
objects: ObjectsState,
}
impl<'r, 'd, D: Copy, G: Copy> TimestampCheck<'r, 'd, D, G> {
#[must_use]
pub fn node(&self) -> usize {
self.node
}
#[must_use]
pub fn receipt(&self) -> Option<&'r timestamp::Checked<'d, D, G>> {
self.receipt
}
#[must_use]
pub fn timestamp(&self) -> TimestampState {
self.timestamp
}
#[must_use]
pub fn objects(&self) -> ObjectsState {
self.objects
}
}
pub struct Mathematics<'g, 'r, 'd, 'v, 's, N, D, G, S: RangeSource> {
graph: &'g Graph<'v, 's, N, D, G, S>,
checks: OwnedVec<TimestampCheck<'r, 'd, S::Document, S::Generation>>,
all_math: bool,
all_objects: bool,
}
impl<'g, 'r, 'd, 'v, 's, N: Copy, D: Copy + Eq, G: Copy + Eq, S: RangeSource>
Mathematics<'g, 'r, 'd, 'v, 's, N, D, G, S>
{
#[must_use]
pub fn graph(&self) -> &'g Graph<'v, 's, N, D, G, S> {
self.graph
}
#[must_use]
pub fn checks(&self) -> &[TimestampCheck<'r, 'd, S::Document, S::Generation>] {
&self.checks
}
#[must_use]
pub fn all_timestamp_math(&self) -> bool {
self.all_math
}
#[must_use]
pub fn all_object_digests(&self) -> bool {
self.all_objects
}
}
impl<'v, 's, N: Copy, D: Copy + Eq, G: Copy + Eq, S: RangeSource> Graph<'v, 's, N, D, G, S> {
pub fn bind_timestamps<'g, 'r, 'd, W: Work>(
&'g self,
supplied: &[&'r timestamp::Checked<'d, S::Document, S::Generation>],
work: &W,
) -> ResultOf<Mathematics<'g, 'r, 'd, 'v, 's, N, D, G, S>, S> {
let execution = self.archive.session().query(work);
self.archive
.admit(&execution, self.archive.directory_span())
.map_err(Error::Archive)?;
let refusal = |cause| Error::Archive(resource(self.archive.directory_span(), cause));
for check in supplied {
self.archive
.original(check.token(), &execution)
.map_err(Error::Archive)?;
self.archive
.original(check.input(), &execution)
.map_err(Error::Archive)?;
}
let mut by_input = execution.map();
for (index, check) in supplied.iter().enumerate() {
if by_input
.try_insert((check.token().ordinal(), check.input().ordinal()), index)
.map_err(refusal)?
.is_some()
{
return Err(Error::DuplicateTimestamp(check.token()));
}
}
let mut checks = execution.vector();
checks
.try_reserve_exact(self.nodes.len())
.map_err(refusal)?;
let mut all_math = !self.nodes.is_empty();
let mut all_objects = !self.nodes.is_empty();
for (index, node) in self.nodes.iter().enumerate() {
let receipt = match node.timestamp {
Some(token) => by_input
.get(&(token.ordinal(), node.member.ordinal()), &execution)
.map_err(refusal)?
.map(|index| supplied[*index]),
None => None,
};
execution.charge_work(1).map_err(refusal)?;
let timestamp = match receipt {
None => TimestampState::Unchecked,
Some(check) => {
if check.common().evidence().is_some() {
TimestampState::Proven
} else {
TimestampState::Failed
}
}
};
let objects = match node
.facts
.and_then(|index| self.facts[index].object_digests())
{
None => ObjectsState::Unchecked,
Some(answers) => {
let mut matched = !answers.is_empty();
for answer in answers {
execution.charge_work(1).map_err(refusal)?;
if *answer != DigestReading::Checked(Agreement::Matches) {
matched = false;
}
}
if matched {
ObjectsState::Matches
} else {
ObjectsState::Failed
}
}
};
if timestamp != TimestampState::Proven {
all_math = false;
}
if objects != ObjectsState::Matches {
all_objects = false;
}
checks
.try_push(TimestampCheck {
node: index,
receipt,
timestamp,
objects,
})
.map_err(refusal)?;
}
self.archive
.admit(&execution, self.archive.directory_span())
.map_err(Error::Archive)?;
Ok(Mathematics {
graph: self,
checks: checks.into_owned(),
all_math,
all_objects,
})
}
}