structured_xades/timestamp/
selection.rs1use super::{InputError, Role};
4use crate::binary::Location;
5use crate::prepare::Prepared;
6use crate::xml::{Access, Xml};
7use crate::{ISSUER, Xades};
8use structured_asn1::memory::{MemoryError, Work};
9use structured_ltv::timestamp::{RequiredToken, TimestampRequirements, TimestampScope};
10
11#[derive(Debug, Clone, Copy)]
14pub enum TimestampSelection {
15 All,
17 BeforeArchive(usize),
19}
20
21impl TimestampSelection {
22 fn includes(self, ordinal: usize, role: Role) -> bool {
23 match self {
24 Self::All => true,
25 Self::BeforeArchive(archive) => {
26 ordinal < archive
27 || matches!(role, Role::AllDataObjects | Role::IndividualDataObjects)
28 }
29 }
30 }
31}
32
33#[derive(Debug)]
35pub enum RequirementError<E, N> {
36 Input(InputError<E, N>),
38 Coverage(super::CoverageError<E, N>),
40 Selection(structured_ltv::material::CoverageError<N>),
42 Index {
44 signature: N,
46 index: usize,
48 },
49 Role {
51 property: N,
53 },
54}
55
56impl<E, N> From<InputError<E, N>> for RequirementError<E, N> {
57 fn from(cause: InputError<E, N>) -> Self {
58 Self::Input(cause)
59 }
60}
61
62pub type RequirementResult<X> = Result<
64 TimestampRequirements<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation, Xades>,
65 RequirementError<<X as Xml>::Error, <X as Xml>::Id>,
66>;
67
68type Entries<X, W> = structured_asn1::memory::OwnedVec<RequiredToken<<X as Xml>::Id>, W>;
69
70pub fn requirements<X: Xml, W: Work>(
84 access: &Access<'_, X, W>,
85 prepared: &Prepared<X>,
86 selection: TimestampSelection,
87) -> RequirementResult<X> {
88 let (scope, entries) = root_entries(access, prepared, selection)?;
89 let signature = prepared.bound().containers().signature();
90 Ok(TimestampRequirements::new(
91 ISSUER,
92 signature,
93 scope,
94 entries.into_owned(),
95 ))
96}
97
98type RootEntries<X, W> = Result<
99 (
100 TimestampScope<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
101 Entries<X, W>,
102 ),
103 RequirementError<<X as Xml>::Error, <X as Xml>::Id>,
104>;
105
106fn root_entries<X: Xml, W: Work>(
107 access: &Access<'_, X, W>,
108 prepared: &Prepared<X>,
109 selection: TimestampSelection,
110) -> RootEntries<X, W> {
111 let layout = prepared.bound().containers();
112 let signature = layout.signature();
113 if layout.snapshot() != access.snapshot() {
114 return Err(InputError::Foreign { node: signature }.into());
115 }
116 let pay = || {
117 access
118 .query(signature, |_, _, work| Ok(work.charge_work(1)))
119 .map_err(InputError::Access)?
120 .map_err(|cause| InputError::Resource {
121 node: signature,
122 cause,
123 })
124 };
125 pay()?;
126 let scope = match selection {
127 TimestampSelection::All => TimestampScope::All(Location {
128 snapshot: access.snapshot(),
129 node: signature,
130 }),
131 TimestampSelection::BeforeArchive(index) => {
132 let Some(selected) = prepared.timestamps().get(index) else {
133 return Err(RequirementError::Index { signature, index });
134 };
135 if selected.role() != Role::Archive {
136 return Err(RequirementError::Role {
137 property: selected.node(),
138 });
139 }
140 TimestampScope::BeforeArchive(Location {
141 snapshot: access.snapshot(),
142 node: selected.node(),
143 })
144 }
145 };
146 let resource = |cause| InputError::Resource {
147 node: scope.location().node,
148 cause,
149 };
150 let mut count = 0usize;
151 for (ordinal, stamp) in prepared.timestamps().iter().enumerate() {
152 pay()?;
153 if selection.includes(ordinal, stamp.role()) {
154 count = count
155 .checked_add(stamp.tokens().len())
156 .ok_or_else(|| resource(MemoryError::WorkExceeded))?;
157 }
158 }
159 let mut entries = access.execution().vector();
160 entries.try_reserve_exact(count).map_err(resource)?;
161 for (ordinal, stamp) in prepared.timestamps().iter().enumerate() {
162 pay()?;
163 if !selection.includes(ordinal, stamp.role()) {
164 continue;
165 }
166 for token in stamp.tokens() {
167 pay()?;
168 entries
169 .try_push(RequiredToken {
170 signature,
171 property: stamp.node(),
172 token: *token,
173 purpose: stamp.role().purpose(),
174 })
175 .map_err(resource)?;
176 }
177 }
178 access
179 .query(signature, |_, _, _| Ok(()))
180 .map_err(InputError::Access)?;
181 Ok((scope, entries))
182}
183
184pub fn requirements_with_counters<X: super::TimestampXml, W: Work>(
190 access: &Access<'_, X, W>,
191 prepared: &Prepared<X>,
192 counters: &super::CounterTimestamps<X::Id, X::Document, X::Generation>,
193 selection: TimestampSelection,
194) -> RequirementResult<X> {
195 let signature = prepared.bound().containers().signature();
196 let resource = |cause| InputError::Resource {
197 node: signature,
198 cause,
199 };
200 if counters.snapshot != access.snapshot()
201 || !access
202 .equal_id(counters.signature, signature)
203 .map_err(resource)?
204 {
205 return Err(InputError::Foreign { node: signature }.into());
206 }
207 let (scope, mut entries) = root_entries(access, prepared, selection)?;
208 let mut signatures = access.execution().vector();
209 signatures
210 .try_extend_from_slice(&counters.signatures)
211 .map_err(resource)?;
212 let coverage = match selection {
213 TimestampSelection::All => None,
214 TimestampSelection::BeforeArchive(index) => {
215 let input = super::Input::prepare(
217 access.owner(),
218 prepared.bound(),
219 &prepared.timestamps()[index],
220 access.execution(),
221 )?;
222 Some(
223 input
224 .element_coverage(access.execution())
225 .map_err(RequirementError::Coverage)?,
226 )
227 }
228 };
229 let covered = if let Some(coverage) = &coverage {
235 let mut mask = access.execution().vector();
236 let mut index = 0;
237 while index < signatures.len() {
238 access
239 .query(signature, |_, _, work| Ok(work.charge_work(1)))
240 .map_err(InputError::Access)?
241 .map_err(resource)?;
242 let included = coverage
243 .selects(signatures[index], access)
244 .map_err(RequirementError::Selection)?;
245 mask.try_push(included).map_err(resource)?;
246 index += 1;
247 }
248 Some(mask.into_owned())
249 } else {
250 None
251 };
252 for child in counters.entries() {
253 let stamp = child.stamp();
254 access
255 .query(stamp.node(), |_, _, work| Ok(work.charge_work(1)))
256 .map_err(InputError::Access)?
257 .map_err(resource)?;
258 if let Some(coverage) = &coverage
259 && !coverage
260 .selects(stamp.node(), access)
261 .map_err(RequirementError::Selection)?
262 {
263 continue;
264 }
265 for token in stamp.tokens() {
266 access
267 .query(*token, |_, _, work| Ok(work.charge_work(1)))
268 .map_err(InputError::Access)?
269 .map_err(resource)?;
270 entries
271 .try_push(RequiredToken {
272 signature: stamp.signature,
273 property: stamp.node(),
274 token: *token,
275 purpose: stamp.role().purpose(),
276 })
277 .map_err(resource)?;
278 }
279 }
280 access
281 .query(signature, |_, _, _| Ok(()))
282 .map_err(InputError::Access)?;
283 Ok(TimestampRequirements::with_counters(
284 ISSUER,
285 signature,
286 scope,
287 entries.into_owned(),
288 signatures.into_owned(),
289 covered,
290 ))
291}