use crate::certificate::{CertificateXml, KeyAssociation};
use crate::counter;
use crate::xml::{Access, AccessError, AccessResult, Snapshot, Xml};
use structured_asn1::memory::{OwnedVec, Work};
use structured_ltv::pki::{CheckedPath, Counter, CounterReceipts};
pub struct CounterPath<'view, 'document, N, D, G> {
counter: counter::Checked<N, D, G>,
path: &'view CheckedPath<'document>,
}
impl<'view, 'document, N, D, G> CounterPath<'view, 'document, N, D, G> {
#[must_use]
pub fn counter(&self) -> &counter::Checked<N, D, G> {
&self.counter
}
#[must_use]
pub fn path(&self) -> &'view CheckedPath<'document> {
self.path
}
}
pub enum CounterBinding<'view, 'document, N, D, G> {
Bound(CounterPath<'view, 'document, N, D, G>),
Differs,
Unproven,
}
pub type CounterPathResult<'view, 'document, X> = AccessResult<
CounterBinding<'view, 'document, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
X,
>;
pub struct CounterPaths<'view, 'document, N, D, G> {
snapshot: Snapshot<D, G>,
signature: N,
actors: OwnedVec<CounterActor<'view, 'document, N, D, G>>,
}
pub struct CounterActor<'view, 'document, N, D, G> {
signer: CounterPath<'view, 'document, N, D, G>,
containers: Option<crate::container::Containers<N, D, G>>,
roles: Option<crate::role::Roles<N, D, G>>,
}
impl<'view, 'document, N, D, G> CounterActor<'view, 'document, N, D, G> {
#[must_use]
pub fn signer(&self) -> &CounterPath<'view, 'document, N, D, G> {
&self.signer
}
#[must_use]
pub fn roles(&self) -> Option<&crate::role::Roles<N, D, G>> {
self.roles.as_ref()
}
#[must_use]
pub fn containers(&self) -> Option<&crate::container::Containers<N, D, G>> {
self.containers.as_ref()
}
}
impl<'view, 'document, N: Copy, D: Copy, G: Copy> CounterPaths<'view, 'document, N, D, G> {
#[must_use]
pub fn snapshot(&self) -> Snapshot<D, G> {
self.snapshot
}
#[must_use]
pub fn signature(&self) -> N {
self.signature
}
#[must_use]
pub fn actors(&self) -> &[CounterActor<'view, 'document, N, D, G>] {
&self.actors
}
}
impl<'view, 'document, N: Copy, D: Copy, G: Copy> CounterReceipts<'view, 'document>
for CounterPaths<'view, 'document, N, D, G>
{
type Id = N;
type Document = D;
type Generation = G;
fn snapshot(&self) -> Snapshot<D, G> {
self.snapshot
}
fn signature(&self) -> N {
self.signature
}
fn len(&self) -> usize {
self.actors.len()
}
fn counter(&self, ordinal: usize) -> Counter<'view, 'document, N> {
let signer = &self.actors[ordinal].signer;
let source = signer.counter.original();
Counter {
signature: source.child(),
property: source.node(),
path: signer.path,
}
}
}
#[derive(Debug)]
pub enum CounterSelectionError<PE, E, N> {
Binding(crate::binding::BindingError<E, N>),
Property(crate::properties::Error<E, N>),
Selection {
signature: N,
cause: PE,
},
Access(AccessError<E, N>),
Differs {
signature: N,
},
Unproven {
signature: N,
},
}
pub type CounterPathsResult<'view, 'document, X, PE> = counter::WalkResult<
CounterPaths<'view, 'document, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
X,
CounterSelectionError<PE, <X as Xml>::Error, <X as Xml>::Id>,
>;
pub fn bind_counters<'view, 'document, X: CertificateXml, W: Work, PE>(
root: &crate::binding::Selected<X>,
access: &Access<'_, X, W>,
mut select: impl FnMut(&counter::Binding<X>, &W) -> Result<&'view CheckedPath<'document>, PE>,
) -> CounterPathsResult<'view, 'document, X, PE> {
let actors = counter::walk(access, root, |counter, layout| {
let signature = counter.original().child();
let roles = match layout {
Some(layout) => counter_roles(access, layout)?,
None => None,
};
let path = access
.query(signature, |_, _, work| Ok(select(counter, work)))
.map_err(CounterSelectionError::Access)?
.map_err(|cause| CounterSelectionError::Selection { signature, cause })?;
match bind_counter(path, counter, access).map_err(CounterSelectionError::Access)? {
CounterBinding::Bound(signer) => Ok(CounterActor {
signer,
roles,
containers: layout.copied(),
}),
CounterBinding::Differs => Err(CounterSelectionError::Differs { signature }),
CounterBinding::Unproven => Err(CounterSelectionError::Unproven { signature }),
}
})?;
Ok(CounterPaths {
snapshot: access.snapshot(),
signature: root.containers().signature(),
actors,
})
}
type RoleResult<X, PE> = Result<
Option<crate::role::Catalogue<X>>,
CounterSelectionError<PE, <X as Xml>::Error, <X as Xml>::Id>,
>;
fn counter_roles<X: CertificateXml, W: Work, PE>(
access: &Access<'_, X, W>,
layout: &crate::container::Layout<X>,
) -> RoleResult<X, PE> {
let Some(node) =
crate::role::locate(access, layout).map_err(CounterSelectionError::Property)?
else {
return Ok(None);
};
let bound = crate::binding::bind(access, layout).map_err(CounterSelectionError::Binding)?;
crate::role::read_node(
access,
layout.snapshot(),
bound.containers().signature(),
node,
)
.map(Some)
.map_err(CounterSelectionError::Property)
}
pub fn bind_counter<'view, 'document, X: CertificateXml, W: Work>(
path: &'view CheckedPath<'document>,
counter: &counter::Binding<X>,
access: &Access<'_, X, W>,
) -> CounterPathResult<'view, 'document, X> {
let source = counter.original();
if source.snapshot() != access.snapshot() {
return Err(AccessError::Changed {
node: source.child(),
});
}
let target = path.members().last().expect("a checked path has a target");
let association = access.query(source.child(), |xml, snapshot, work| {
xml.key_association(
snapshot,
source.child(),
target.certificate(),
path.facts().key,
work,
)
})?;
Ok(match association {
KeyAssociation::Same => CounterBinding::Bound(CounterPath {
counter: *counter,
path,
}),
KeyAssociation::Differs => CounterBinding::Differs,
KeyAssociation::Unproven => CounterBinding::Unproven,
})
}