use super::{Input, Part, TimestampXml};
use crate::xml::{Access, AccessError, Node, Xml};
use crate::{ISSUER, Xades};
use structured_asn1::memory::{MemoryError, Work};
use structured_ltv::material::Coverage;
pub type CoverageFor<X, W> =
Coverage<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation, W, Xades>;
#[derive(Debug)]
pub enum CoverageError<E, N> {
Access(AccessError<E, N>),
Resource {
node: N,
cause: MemoryError,
},
}
type Result<T, X> = core::result::Result<T, CoverageError<<X as Xml>::Error, <X as Xml>::Id>>;
struct Frame<N> {
parent: N,
next: Option<N>,
}
impl<X: TimestampXml> Input<'_, X> {
pub fn element_coverage<W: Work>(&self, execution: &W) -> Result<CoverageFor<X, W>, X> {
let access = Access::new(self.xml, self.snapshot, execution);
access
.query(self.timestamp, |_, _, _| Ok(()))
.map_err(CoverageError::Access)?;
let mut elements = execution.map();
let mut frames = execution.vector();
for ordinal in 0..self.parts.len() {
access
.query(self.timestamp, |_, _, work| Ok(work.charge_work(1)))
.map_err(CoverageError::Access)?
.map_err(|cause| CoverageError::Resource {
node: self.timestamp,
cause,
})?;
let root = match self.parts[ordinal] {
Part::Element(root)
| Part::Reference {
covered: Some(root),
..
} => root,
Part::Reference { covered: None, .. } => continue,
};
let resource = |cause| CoverageError::Resource { node: root, cause };
if elements.get(&root, execution).map_err(resource)?.is_some() {
continue;
}
let element = access
.inspect(root, |value| matches!(value, Node::Element(_)))
.map_err(CoverageError::Access)?;
elements.try_insert(root, element).map_err(resource)?;
let next = access.first_child(root).map_err(CoverageError::Access)?;
frames
.try_push(Frame { parent: root, next })
.map_err(resource)?;
while let Some(frame) = frames.last_mut() {
let Some(node) = frame.next else {
frames.pop();
continue;
};
frame.next = access
.next_sibling(frame.parent, node)
.map_err(CoverageError::Access)?;
let element = access
.inspect(node, |value| matches!(value, Node::Element(_)))
.map_err(CoverageError::Access)?;
if !element {
continue;
}
if elements.get(&node, execution).map_err(resource)?.is_some() {
continue;
}
elements.try_insert(node, true).map_err(resource)?;
let next = access.first_child(node).map_err(CoverageError::Access)?;
frames
.try_push(Frame { parent: node, next })
.map_err(resource)?;
}
}
access
.query(self.timestamp, |_, _, _| Ok(()))
.map_err(CoverageError::Access)?;
Ok(Coverage::new(
ISSUER,
self.snapshot,
self.signature,
self.timestamp,
self.role.purpose(),
elements,
))
}
}