1use crate::container::{Containers, Layout};
4use crate::xml::{Access, AccessError, Snapshot, Xml};
5use structured_asn1::memory::Work;
6
7pub const SIGNED_PROPERTIES: &str = "http://uri.etsi.org/01903#SignedProperties";
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum Status {
13 Valid,
15 Invalid,
17 Unchecked,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum Coverage {
24 ElementContent,
26 Transformed,
28}
29
30#[derive(Debug, Clone, Copy)]
32pub struct Reference<'a, N> {
33 pub node: N,
35 pub target: Option<N>,
37 pub kind: Option<&'a str>,
39 pub status: Status,
41 pub coverage: Coverage,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum Target<N> {
48 LocalBareName(N),
50 Missing,
52 Other,
54}
55
56pub trait SignatureXml: Xml {
64 fn signature_status<W: Work>(
67 &self,
68 snapshot: Snapshot<Self::Document, Self::Generation>,
69 signature: Self::Id,
70 execution: &W,
71 ) -> Result<Status, Self::Error>;
72
73 fn reference_count<W: Work>(
75 &self,
76 snapshot: Snapshot<Self::Document, Self::Generation>,
77 signature: Self::Id,
78 execution: &W,
79 ) -> Result<usize, Self::Error>;
80
81 fn reference<R, W: Work>(
83 &self,
84 snapshot: Snapshot<Self::Document, Self::Generation>,
85 signature: Self::Id,
86 ordinal: usize,
87 execution: &W,
88 visitor: impl for<'view> FnOnce(Reference<'view, Self::Id>) -> R,
89 ) -> Result<R, Self::Error>;
90
91 fn qualifying_target<W: Work>(
95 &self,
96 snapshot: Snapshot<Self::Document, Self::Generation>,
97 qualifying: Self::Id,
98 execution: &W,
99 ) -> Result<Target<Self::Id>, Self::Error>;
100}
101
102#[derive(Debug)]
104pub enum BindingError<E, N> {
105 Access(AccessError<E, N>),
107 Signature {
109 node: N,
111 status: Status,
113 },
114 Target {
116 node: N,
118 },
119 Missing {
121 node: N,
123 },
124 Reference {
126 node: N,
128 },
129 References {
131 node: N,
133 },
134}
135
136#[derive(Debug)]
141pub struct Bound<N, D, G> {
142 containers: Containers<N, D, G>,
143 reference: N,
144 ordinal: usize,
145}
146
147impl<N: Copy, D: Copy, G: Copy> Bound<N, D, G> {
148 #[must_use]
150 pub fn containers(&self) -> &Containers<N, D, G> {
151 &self.containers
152 }
153 #[must_use]
155 pub fn reference(&self) -> N {
156 self.reference
157 }
158 #[must_use]
160 pub fn ordinal(&self) -> usize {
161 self.ordinal
162 }
163}
164
165pub type Selected<X> = Bound<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
167pub type BindingResult<T, X> = Result<T, BindingError<<X as Xml>::Error, <X as Xml>::Id>>;
169
170#[derive(Clone, Copy)]
171pub(crate) enum Purpose<N> {
172 Properties(N),
173 Counter(N),
174}
175
176pub(crate) fn covering_reference<X: SignatureXml, W: Work>(
177 access: &Access<'_, X, W>,
178 signature: X::Id,
179 purpose: Purpose<X::Id>,
180) -> BindingResult<(X::Id, usize), X> {
181 let status = access
182 .query(signature, |xml, snapshot, work| {
183 xml.signature_status(snapshot, signature, work)
184 })
185 .map_err(BindingError::Access)?;
186 if status != Status::Valid {
187 return Err(BindingError::Signature {
188 node: signature,
189 status,
190 });
191 }
192 let count = access
193 .query(signature, |xml, snapshot, work| {
194 xml.reference_count(snapshot, signature, work)
195 })
196 .map_err(BindingError::Access)?;
197 let (wanted, minimum) = match purpose {
198 Purpose::Properties(target) => (target, 2),
199 Purpose::Counter(target) => (target, 1),
200 };
201 if count < minimum {
202 return Err(BindingError::References { node: signature });
203 }
204 let mut selected = None;
205 let mut uncovered = None;
206 for ordinal in 0..count {
207 let (node, target, status, coverage, eligible) = access
208 .query(signature, |xml, snapshot, work| {
209 xml.reference(snapshot, signature, ordinal, work, |r| {
210 let eligible = match purpose {
211 Purpose::Properties(_) => match r.kind {
212 Some(kind) => access.equal(kind, SIGNED_PROPERTIES),
213 None => Ok(false),
214 },
215 Purpose::Counter(_) => Ok(true),
220 };
221 (r.node, r.target, r.status, r.coverage, eligible)
222 })
223 })
224 .map_err(BindingError::Access)?;
225 let eligible = eligible
226 .map_err(|cause| BindingError::Access(AccessError::Resource { node, cause }))?;
227 if !eligible {
228 continue;
229 }
230 let matches = match target {
231 Some(target) => access
232 .equal_id(target, wanted)
233 .map_err(|cause| BindingError::Access(AccessError::Resource { node, cause }))?,
234 None => false,
235 };
236 if !matches {
237 if let Purpose::Counter(_) = purpose {
238 continue;
239 }
240 return Err(BindingError::Reference { node });
241 }
242 if status != Status::Valid {
243 return Err(BindingError::Reference { node });
244 }
245 if coverage != Coverage::ElementContent {
246 uncovered = Some(node);
247 continue;
248 }
249 if selected.is_none() {
250 selected = Some((node, ordinal));
251 }
252 }
253 if let Some(value) = selected {
254 return Ok(value);
255 }
256 if let Some(node) = uncovered {
257 return Err(BindingError::Reference { node });
258 }
259 Err(BindingError::Missing { node: wanted })
260}
261
262pub fn bind<X: SignatureXml, W: Work>(
264 access: &Access<'_, X, W>,
265 layout: &Layout<X>,
266) -> BindingResult<Selected<X>, X> {
267 check_target(access, layout)?;
268 let (reference, ordinal) = covering_reference(
269 access,
270 layout.signature(),
271 Purpose::Properties(layout.signed()),
272 )?;
273 Ok(Bound {
274 containers: *layout,
275 reference,
276 ordinal,
277 })
278}
279
280pub(crate) fn check_target<X: SignatureXml, W: Work>(
286 access: &Access<'_, X, W>,
287 layout: &Layout<X>,
288) -> BindingResult<(), X> {
289 let signature = layout.signature();
290 if access.snapshot() != layout.snapshot() {
291 return Err(BindingError::Access(AccessError::Changed {
292 node: signature,
293 }));
294 }
295 let target = access
298 .query(layout.qualifying(), |xml, snapshot, work| {
299 xml.qualifying_target(snapshot, layout.qualifying(), work)
300 })
301 .map_err(BindingError::Access)?;
302 let matched = match target {
303 Target::LocalBareName(target) => access.equal_id(target, signature).map_err(|cause| {
304 BindingError::Access(AccessError::Resource {
305 node: layout.qualifying(),
306 cause,
307 })
308 })?,
309 Target::Missing | Target::Other => false,
310 };
311 if !matched {
312 return Err(BindingError::Target {
313 node: layout.qualifying(),
314 });
315 }
316 Ok(())
317}