use crate::Xades;
use structured_asn1::memory::{MemoryError, OwnedVec, Work};
use structured_ltv::material::{
ArtifactRef, ArtifactUses,
status::{self, StatusPart},
};
use structured_ltv::pki::{Participant, ParticipantAuthority, PathStatuses};
pub struct PathStatusSource<'certificate, 'document> {
pub(super) artifact: ArtifactRef<'certificate, 'document>,
member: usize,
contributor: usize,
}
impl<'c, 'd> PathStatusSource<'c, 'd> {
#[must_use]
pub fn artifact(&self) -> ArtifactRef<'c, 'd> {
self.artifact
}
#[must_use]
pub fn member(&self) -> usize {
self.member
}
#[must_use]
pub fn contributor(&self) -> usize {
self.contributor
}
}
pub(super) struct Required<'c, 'd, W: Work> {
pub(super) sources: OwnedVec<PathStatusSource<'c, 'd>, W>,
pub(super) missing_input: bool,
}
pub(super) fn collect<'c, 'd, N: Copy, D: Copy, G: Copy, W: Work>(
artifacts: &ArtifactUses<'_, '_, '_, 'c, 'd, N, D, G, Xades>,
work: &W,
) -> Result<Required<'c, 'd, W>, MemoryError> {
work.charge_work(1)?;
let inputs = artifacts.inputs();
let root = inputs
.requirements()
.entries()
.first()
.expect("constructed signer requirement");
assert!(
matches!(root.participant(), Participant::Signer(_)),
"signer is first"
);
let mut sources = work.vector();
let mut missing_input = false;
if let ParticipantAuthority::Path(path) = root.authority() {
let statuses = inputs
.authorities()
.roots()
.first()
.expect("signer status root");
assert!(
core::ptr::eq(statuses.path(), path),
"constructed root association"
);
missing_input = append_path(statuses, work, |source| sources.try_push(source))?;
}
Ok(Required {
sources,
missing_input,
})
}
fn append_path<'c, 'd, W: Work>(
statuses: &PathStatuses<'_, 'c, 'd>,
work: &W,
mut record: impl FnMut(PathStatusSource<'c, 'd>) -> Result<(), MemoryError>,
) -> Result<bool, MemoryError> {
let mut missing_input = false;
for (member, entry) in statuses.entries().iter().enumerate() {
work.charge_work(1)?;
let Some(source) = status::member_source(entry) else {
missing_input = true;
continue;
};
status::visit_artifacts_in(source, work, |artifact, contributor, part| {
if part != StatusPart::SigningCertificate {
record(PathStatusSource {
artifact,
member,
contributor,
})?;
}
Ok::<_, MemoryError>(())
})?;
}
Ok(missing_input)
}
pub struct CredentialStatusSource<'c, 'd> {
participant: usize,
pub(super) source: PathStatusSource<'c, 'd>,
pub(super) ordinary: bool,
pub(super) attribute: bool,
}
impl<'c, 'd> CredentialStatusSource<'c, 'd> {
#[must_use]
pub fn participant(&self) -> usize {
self.participant
}
#[must_use]
pub fn source(&self) -> &PathStatusSource<'c, 'd> {
&self.source
}
#[must_use]
pub fn presence(&self) -> (bool, bool) {
(self.ordinary, self.attribute)
}
#[must_use]
pub fn redundant_attribute_reference(&self) -> bool {
self.ordinary && self.attribute
}
}
pub(super) struct CredentialRequired<'c, 'd, W: Work> {
pub(super) sources: OwnedVec<CredentialStatusSource<'c, 'd>, W>,
pub(super) missing_input: bool,
}
pub(super) fn collect_credentials<'c, 'd, N: Copy, D: Copy, G: Copy, W: Work>(
artifacts: &ArtifactUses<'_, '_, '_, 'c, 'd, N, D, G, Xades>,
work: &W,
) -> Result<CredentialRequired<'c, 'd, W>, MemoryError> {
let inputs = artifacts.inputs();
let mut roots = inputs.authorities().roots().iter();
let mut sources = work.vector();
let mut missing_input = false;
for (participant, entry) in inputs.requirements().entries().iter().enumerate() {
work.charge_work(1)?;
let ParticipantAuthority::Path(path) = entry.authority() else {
continue;
};
let statuses = roots
.next()
.expect("constructed complete path/status pairing");
assert!(
core::ptr::eq(statuses.path(), path),
"same constructed path receipt"
);
if !matches!(entry.participant(), Participant::Credential { .. }) {
continue;
}
missing_input |= append_path(statuses, work, |source| {
sources.try_push(CredentialStatusSource {
participant,
source,
ordinary: false,
attribute: false,
})
})?;
}
Ok(CredentialRequired {
sources,
missing_input,
})
}