use super::BasicStage;
use crate::pki::SignerReceipt;
use crate::source::{Query, ScopeError};
use crate::timestamp::{
Purpose, SignatureTime, SignatureTimeBinding, SignatureTimeError, SignatureTimeFailure,
TimestampPaths, TimestampScope, signature_time_path,
};
use structured_asn1::memory::{MemoryError, OwnedVec, Work};
pub enum SignatureTimeFact<'a, S, N, D, G, I> {
Bound(SignatureTime<'a, S, N, D, G, I>),
Unproven {
property: N,
cause: SignatureTimeFailure,
},
}
pub struct Timed<'a, 'certificate, 'document, B, S, N, D, G, I> {
basic: &'a B,
paths: &'a TimestampPaths<'a, 'certificate, 'document, N, D, G, I>,
signatures: OwnedVec<SignatureTimeFact<'a, S, N, D, G, I>>,
static_complete: bool,
}
impl<'a, 'c, 'd, B, S, N, D, G, I> Timed<'a, 'c, 'd, B, S, N, D, G, I> {
#[must_use]
pub fn basic(&self) -> &'a B {
self.basic
}
#[must_use]
pub fn paths(&self) -> &'a TimestampPaths<'a, 'c, 'd, N, D, G, I> {
self.paths
}
#[must_use]
pub fn signatures(&self) -> &[SignatureTimeFact<'a, S, N, D, G, I>] {
&self.signatures
}
#[must_use]
pub fn all_static_time_requirements_met(&self) -> bool {
self.static_complete
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimeError<N> {
Foreign {
signature: N,
},
Scope(ScopeError<N>),
Signature(SignatureTimeError<N>),
Resource {
node: N,
cause: MemoryError,
},
}
pub type TimeResult<'a, 'c, 'd, B, S, Q, I> = Result<
Timed<'a, 'c, 'd, B, S, <Q as Query>::Id, <Q as Query>::Document, <Q as Query>::Generation, I>,
TimeError<<Q as Query>::Id>,
>;
pub fn timestamped<'a, 'c, 'd, 'path, 'document, B, Q, I>(
basic: &'a B,
paths: &'a TimestampPaths<'a, 'c, 'd, Q::Id, Q::Document, Q::Generation, I>,
query: &Q,
) -> TimeResult<'a, 'c, 'd, B, B::Signer, Q, I>
where
'document: 'path,
B: BasicStage<'path, 'document, Id = Q::Id, Document = Q::Document, Generation = Q::Generation>,
Q: Query,
{
let selected = basic.signer().signature();
let signature = selected.node;
let required = paths.required();
let resource = |cause| TimeError::Resource {
node: signature,
cause,
};
if !matches!(required.scope(), TimestampScope::All(_))
|| required.scope().location().snapshot != query.snapshot()
|| selected.snapshot != query.snapshot()
|| !query
.equal(required.signature(), signature)
.map_err(resource)?
|| !required
.includes_counters(basic.counters(), query)
.map_err(TimeError::Scope)?
{
return Err(TimeError::Foreign { signature });
}
query.admit(signature, 1).map_err(TimeError::Scope)?;
let mut signatures = query.execution().vector();
let mut static_complete = basic.all_requirements_met();
for path in paths.paths() {
query.execution().charge_work(1).map_err(resource)?;
let verified = path.actor().verified();
if verified.purpose() != Purpose::Signature
|| !query
.equal(verified.signature(), signature)
.map_err(resource)?
{
continue;
}
signatures.try_reserve(1).map_err(resource)?;
let fact =
match signature_time_path(basic.signer(), path, query).map_err(TimeError::Signature)? {
SignatureTimeBinding::Bound(value) => SignatureTimeFact::Bound(value),
SignatureTimeBinding::Unproven(cause) => {
static_complete = false;
SignatureTimeFact::Unproven {
property: verified.property(),
cause,
}
}
};
signatures.try_push(fact).map_err(resource)?;
}
if signatures.is_empty() {
static_complete = false;
}
query.admit(signature, 1).map_err(TimeError::Scope)?;
Ok(Timed {
basic,
paths,
signatures: signatures.into_owned(),
static_complete,
})
}