1use crate::certificate::{CertificateXml, KeyAssociation};
4use crate::counter;
5use crate::xml::{Access, AccessError, AccessResult, Snapshot, Xml};
6use structured_asn1::memory::{OwnedVec, Work};
7use structured_ltv::pki::{CheckedPath, Counter, CounterReceipts};
8
9pub struct CounterPath<'view, 'document, N, D, G> {
17 counter: counter::Checked<N, D, G>,
18 path: &'view CheckedPath<'document>,
19}
20
21impl<'view, 'document, N, D, G> CounterPath<'view, 'document, N, D, G> {
22 #[must_use]
24 pub fn counter(&self) -> &counter::Checked<N, D, G> {
25 &self.counter
26 }
27
28 #[must_use]
30 pub fn path(&self) -> &'view CheckedPath<'document> {
31 self.path
32 }
33}
34
35pub enum CounterBinding<'view, 'document, N, D, G> {
37 Bound(CounterPath<'view, 'document, N, D, G>),
39 Differs,
41 Unproven,
43}
44
45pub type CounterPathResult<'view, 'document, X> = AccessResult<
47 CounterBinding<'view, 'document, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
48 X,
49>;
50
51pub struct CounterPaths<'view, 'document, N, D, G> {
55 snapshot: Snapshot<D, G>,
56 signature: N,
57 actors: OwnedVec<CounterActor<'view, 'document, N, D, G>>,
58}
59
60pub struct CounterActor<'view, 'document, N, D, G> {
64 signer: CounterPath<'view, 'document, N, D, G>,
65 containers: Option<crate::container::Containers<N, D, G>>,
66 roles: Option<crate::role::Roles<N, D, G>>,
67}
68
69impl<'view, 'document, N, D, G> CounterActor<'view, 'document, N, D, G> {
70 #[must_use]
72 pub fn signer(&self) -> &CounterPath<'view, 'document, N, D, G> {
73 &self.signer
74 }
75 #[must_use]
77 pub fn roles(&self) -> Option<&crate::role::Roles<N, D, G>> {
78 self.roles.as_ref()
79 }
80 #[must_use]
83 pub fn containers(&self) -> Option<&crate::container::Containers<N, D, G>> {
84 self.containers.as_ref()
85 }
86}
87
88impl<'view, 'document, N: Copy, D: Copy, G: Copy> CounterPaths<'view, 'document, N, D, G> {
89 #[must_use]
91 pub fn snapshot(&self) -> Snapshot<D, G> {
92 self.snapshot
93 }
94 #[must_use]
96 pub fn signature(&self) -> N {
97 self.signature
98 }
99 #[must_use]
101 pub fn actors(&self) -> &[CounterActor<'view, 'document, N, D, G>] {
102 &self.actors
103 }
104}
105
106impl<'view, 'document, N: Copy, D: Copy, G: Copy> CounterReceipts<'view, 'document>
108 for CounterPaths<'view, 'document, N, D, G>
109{
110 type Id = N;
111 type Document = D;
112 type Generation = G;
113
114 fn snapshot(&self) -> Snapshot<D, G> {
115 self.snapshot
116 }
117
118 fn signature(&self) -> N {
119 self.signature
120 }
121
122 fn len(&self) -> usize {
123 self.actors.len()
124 }
125
126 fn counter(&self, ordinal: usize) -> Counter<'view, 'document, N> {
127 let signer = &self.actors[ordinal].signer;
128 let source = signer.counter.original();
129 Counter {
130 signature: source.child(),
131 property: source.node(),
132 path: signer.path,
133 }
134 }
135}
136
137#[derive(Debug)]
139pub enum CounterSelectionError<PE, E, N> {
140 Binding(crate::binding::BindingError<E, N>),
142 Property(crate::properties::Error<E, N>),
144 Selection {
146 signature: N,
148 cause: PE,
150 },
151 Access(AccessError<E, N>),
153 Differs {
155 signature: N,
157 },
158 Unproven {
160 signature: N,
162 },
163}
164
165pub type CounterPathsResult<'view, 'document, X, PE> = counter::WalkResult<
167 CounterPaths<'view, 'document, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
168 X,
169 CounterSelectionError<PE, <X as Xml>::Error, <X as Xml>::Id>,
170>;
171
172pub fn bind_counters<'view, 'document, X: CertificateXml, W: Work, PE>(
185 root: &crate::binding::Selected<X>,
186 access: &Access<'_, X, W>,
187 mut select: impl FnMut(&counter::Binding<X>, &W) -> Result<&'view CheckedPath<'document>, PE>,
188) -> CounterPathsResult<'view, 'document, X, PE> {
189 let actors = counter::walk(access, root, |counter, layout| {
190 let signature = counter.original().child();
191 let roles = match layout {
192 Some(layout) => counter_roles(access, layout)?,
193 None => None,
194 };
195 let path = access
196 .query(signature, |_, _, work| Ok(select(counter, work)))
197 .map_err(CounterSelectionError::Access)?
198 .map_err(|cause| CounterSelectionError::Selection { signature, cause })?;
199 match bind_counter(path, counter, access).map_err(CounterSelectionError::Access)? {
200 CounterBinding::Bound(signer) => Ok(CounterActor {
201 signer,
202 roles,
203 containers: layout.copied(),
204 }),
205 CounterBinding::Differs => Err(CounterSelectionError::Differs { signature }),
206 CounterBinding::Unproven => Err(CounterSelectionError::Unproven { signature }),
207 }
208 })?;
209 Ok(CounterPaths {
210 snapshot: access.snapshot(),
211 signature: root.containers().signature(),
212 actors,
213 })
214}
215
216type RoleResult<X, PE> = Result<
217 Option<crate::role::Catalogue<X>>,
218 CounterSelectionError<PE, <X as Xml>::Error, <X as Xml>::Id>,
219>;
220
221fn counter_roles<X: CertificateXml, W: Work, PE>(
222 access: &Access<'_, X, W>,
223 layout: &crate::container::Layout<X>,
224) -> RoleResult<X, PE> {
225 let Some(node) =
226 crate::role::locate(access, layout).map_err(CounterSelectionError::Property)?
227 else {
228 return Ok(None);
229 };
230 let bound = crate::binding::bind(access, layout).map_err(CounterSelectionError::Binding)?;
231 crate::role::read_node(
232 access,
233 layout.snapshot(),
234 bound.containers().signature(),
235 node,
236 )
237 .map(Some)
238 .map_err(CounterSelectionError::Property)
239}
240
241pub fn bind_counter<'view, 'document, X: CertificateXml, W: Work>(
256 path: &'view CheckedPath<'document>,
257 counter: &counter::Binding<X>,
258 access: &Access<'_, X, W>,
259) -> CounterPathResult<'view, 'document, X> {
260 let source = counter.original();
261 if source.snapshot() != access.snapshot() {
262 return Err(AccessError::Changed {
263 node: source.child(),
264 });
265 }
266 let target = path.members().last().expect("a checked path has a target");
267 let association = access.query(source.child(), |xml, snapshot, work| {
268 xml.key_association(
269 snapshot,
270 source.child(),
271 target.certificate(),
272 path.facts().key,
273 work,
274 )
275 })?;
276 Ok(match association {
277 KeyAssociation::Same => CounterBinding::Bound(CounterPath {
278 counter: *counter,
279 path,
280 }),
281 KeyAssociation::Differs => CounterBinding::Differs,
282 KeyAssociation::Unproven => CounterBinding::Unproven,
283 })
284}