use crate::{
material::Placement,
query::Query,
range::RangeSource,
timestamp::{Node, Pades, Requirements},
};
use structured_asn1::memory::{MemoryError, OwnedVec, Work};
use structured_ltv::{
baseline::ArchiveMaterial,
material::ParticipantMaterial,
source::{Location, Query as _, ScopeError},
timestamp::TimestampScope,
};
pub type Seal<'a, 'cl, 'r, 'v, 'c, 'd, 'auth, D, G, C> = ArchiveMaterial<
'a,
ParticipantMaterial<'cl, 'r, 'v, 'c, 'd, Node, D, G, Placement, Pades>,
crate::material::credentials::Report<'auth, C, D, G>,
>;
pub type SealReports<'s, 'a, 'cl, 'r, 'v, 'c, 'd, 'auth, D, G, C> =
&'s [Option<&'s Seal<'a, 'cl, 'r, 'v, 'c, 'd, 'auth, D, G, C>>];
pub type SealsOf<'s, 'a, 'cl, 'r, 'v, 'c, 'd, 'auth, D, G, C> =
Seals<'s, Seal<'a, 'cl, 'r, 'v, 'c, 'd, 'auth, D, G, C>, D, G>;
pub struct Seals<'s, R, D, G> {
pub(super) sources: OwnedVec<Occurrence<D, G>>,
pub(super) reports: &'s [Option<&'s R>],
pub(super) required: &'s structured_ltv::timestamp::TimestampRequirements<Node, D, G, Pades>,
pub(super) session: &'s crate::Session,
pub(super) vri_revisions: OwnedVec<usize>,
}
pub(super) struct Occurrence<D, G> {
pub(super) location: Location<Node, D, G>,
pub(super) revision: Option<usize>,
}
impl<'s, R, D: Copy, G: Copy> Seals<'s, R, D, G> {
#[must_use]
pub fn len(&self) -> usize {
self.sources.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.sources.is_empty()
}
#[must_use]
pub fn source(&self, ordinal: usize) -> Option<Location<Node, D, G>> {
self.sources.get(ordinal).map(|source| source.location)
}
#[must_use]
pub fn revision(&self, ordinal: usize) -> Option<usize> {
self.sources.get(ordinal).and_then(|source| source.revision)
}
#[must_use]
pub fn report(&self, ordinal: usize) -> Option<&'s R> {
self.reports.get(ordinal).copied().flatten()
}
}
#[derive(Debug)]
pub enum Error {
Foreign {
node: Node,
},
Scope(ScopeError<Node>),
Resource {
node: Node,
cause: MemoryError,
},
}
pub fn seals<'s, 'a, 'cl, 'r, 'v, 'c, 'd, 'auth, C, S: RangeSource>(
required: &'s Requirements<'_, '_, S::Document, S::Generation>,
reports: SealReports<'s, 'a, 'cl, 'r, 'v, 'c, 'd, 'auth, S::Document, S::Generation, C>,
query: &Query<'_, '_, S>,
) -> Result<SealsOf<'s, 'a, 'cl, 'r, 'v, 'c, 'd, 'auth, S::Document, S::Generation, C>, Error> {
let root = required.all().signature();
query.admit(root, 1).map_err(Error::Scope)?;
if !query.belongs(required.signature().owner().value.session)
|| required.revision().is_some()
|| reports.len() != required.document_timestamps().len()
{
return Err(Error::Foreign { node: root });
}
let mut sources = query.execution().vector();
sources
.try_reserve_exact(reports.len())
.map_err(|cause| Error::Resource { node: root, cause })?;
for (source, report) in required.document_timestamps().iter().zip(reports) {
query.admit(root, 1).map_err(Error::Scope)?;
let node = source.token();
if let Some(report) = report {
let actual = report.x509().inputs().requirements();
if actual.signature() != root
|| !matches!(actual.scope(), TimestampScope::BeforeArchive(location)
if location.snapshot == query.snapshot() && location.node == node)
{
return Err(Error::Foreign { node });
}
}
let revision = source
.signature()
.and_then(|signature| match signature.coverage() {
crate::signature::Coverage::Revision(revision) => Some(revision),
crate::signature::Coverage::Partial => None,
});
sources
.try_push(Occurrence {
location: Location {
snapshot: query.snapshot(),
node,
},
revision,
})
.map_err(|cause| Error::Resource { node, cause })?;
}
query.admit(root, 1).map_err(Error::Scope)?;
let mut vri_revisions = query.execution().vector();
for source in required.vri_timestamps() {
query.admit(root, 1).map_err(Error::Scope)?;
vri_revisions
.try_push(source.revision)
.map_err(|cause| Error::Resource { node: root, cause })?;
}
Ok(Seals {
sources: sources.into_owned(),
reports,
required: required.all(),
session: required.signature().owner().value.session,
vri_revisions: vri_revisions.into_owned(),
})
}