Skip to main content

structured_xades/timestamp/
coverage.rs

1//! Exact original element selection for implicit timestamp input coverage.
2
3use super::{Input, Part, TimestampXml};
4use crate::xml::{Access, AccessError, Node, Xml};
5use crate::{ISSUER, Xades};
6use structured_asn1::memory::{MemoryError, Work};
7use structured_ltv::material::Coverage;
8
9/// Element coverage in the XML owner's original identity domain. Transformed
10/// reference octets grant no whole-element coverage unless the original XML
11/// owner explicitly certified `ElementContent` for that successful reference
12/// and its actual target.
13pub type CoverageFor<X, W> =
14    Coverage<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation, W, Xades>;
15
16/// Refusal while selecting the original elements of one timestamp input.
17#[derive(Debug)]
18pub enum CoverageError<E, N> {
19    /// Actual XML/current-operation processing refused.
20    Access(AccessError<E, N>),
21    /// Element index or iterative traversal storage/work was not admitted.
22    Resource {
23        /// Original root/input node whose admission failed.
24        node: N,
25        /// Original capacity/work refusal.
26        cause: MemoryError,
27    },
28}
29
30type Result<T, X> = core::result::Result<T, CoverageError<<X as Xml>::Error, <X as Xml>::Id>>;
31
32struct Frame<N> {
33    parent: N,
34    next: Option<N>,
35}
36
37impl<X: TimestampXml> Input<'_, X> {
38    /// Indexes the original elements canonicalized in their entirety by this
39    /// prepared input, including nested counter-signature/material enclosures.
40    /// Comments/text are not element claims; arbitrary transformed reference
41    /// output grants no subtree coverage. The same original Part sequence and
42    /// explicit successful `ElementContent` receipts drive replay and selection.
43    /// Iterative O(depth) cursor frames and an identity set avoid recursive
44    /// traversal and repeated scans of overlapping original subtrees.
45    pub fn element_coverage<W: Work>(&self, execution: &W) -> Result<CoverageFor<X, W>, X> {
46        let access = Access::new(self.xml, self.snapshot, execution);
47        access
48            .query(self.timestamp, |_, _, _| Ok(()))
49            .map_err(CoverageError::Access)?;
50        let mut elements = execution.map();
51        let mut frames = execution.vector();
52        for ordinal in 0..self.parts.len() {
53            access
54                .query(self.timestamp, |_, _, work| Ok(work.charge_work(1)))
55                .map_err(CoverageError::Access)?
56                .map_err(|cause| CoverageError::Resource {
57                    node: self.timestamp,
58                    cause,
59                })?;
60            let root = match self.parts[ordinal] {
61                Part::Element(root)
62                | Part::Reference {
63                    covered: Some(root),
64                    ..
65                } => root,
66                Part::Reference { covered: None, .. } => continue,
67            };
68            let resource = |cause| CoverageError::Resource { node: root, cause };
69            if elements.get(&root, execution).map_err(resource)?.is_some() {
70                continue;
71            }
72            // A reference may identify the XML document's virtual root. It
73            // carries descendants but is not itself an element claim. Keep
74            // its visited flag too, avoiding repeated root traversal.
75            let element = access
76                .inspect(root, |value| matches!(value, Node::Element(_)))
77                .map_err(CoverageError::Access)?;
78            elements.try_insert(root, element).map_err(resource)?;
79            let next = access.first_child(root).map_err(CoverageError::Access)?;
80            frames
81                .try_push(Frame { parent: root, next })
82                .map_err(resource)?;
83            while let Some(frame) = frames.last_mut() {
84                let Some(node) = frame.next else {
85                    frames.pop();
86                    continue;
87                };
88                frame.next = access
89                    .next_sibling(frame.parent, node)
90                    .map_err(CoverageError::Access)?;
91                let element = access
92                    .inspect(node, |value| matches!(value, Node::Element(_)))
93                    .map_err(CoverageError::Access)?;
94                if !element {
95                    continue;
96                }
97                if elements.get(&node, execution).map_err(resource)?.is_some() {
98                    continue;
99                }
100                elements.try_insert(node, true).map_err(resource)?;
101                let next = access.first_child(node).map_err(CoverageError::Access)?;
102                frames
103                    .try_push(Frame { parent: node, next })
104                    .map_err(resource)?;
105            }
106        }
107        access
108            .query(self.timestamp, |_, _, _| Ok(()))
109            .map_err(CoverageError::Access)?;
110        Ok(Coverage::new(
111            ISSUER,
112            self.snapshot,
113            self.signature,
114            self.timestamp,
115            self.role.purpose(),
116            elements,
117        ))
118    }
119}