use crate::source::{Query, ScopeError, Snapshot, equal_id};
use crate::timestamp::{Purpose, Verified};
use core::marker::PhantomData;
use structured_asn1::memory::{HashWork, MemoryError, OwnedMap, Work};
pub struct Coverage<N: HashWork, D, G, W: Work, I> {
snapshot: Snapshot<D, G>,
signature: N,
timestamp: N,
token: Option<N>,
purpose: Purpose,
items: OwnedMap<N, bool, W>,
issuer: PhantomData<I>,
}
#[derive(Clone, Copy)]
struct Identity<N, D, G> {
snapshot: Snapshot<D, G>,
signature: N,
property: N,
token: N,
purpose: Purpose,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CoverageError<N> {
Scope(ScopeError<N>),
Resource {
node: N,
cause: MemoryError,
},
Foreign {
node: N,
},
}
impl<N: Copy + Eq + HashWork, D: Copy + Eq, G: Copy + Eq, W: Work, I> Coverage<N, D, G, W, I> {
#[must_use]
pub fn new(
_issuer: I,
snapshot: Snapshot<D, G>,
signature: N,
timestamp: N,
purpose: Purpose,
items: OwnedMap<N, bool, W>,
) -> Self {
Self {
snapshot,
signature,
timestamp,
token: None,
purpose,
items,
issuer: PhantomData,
}
}
#[must_use]
pub fn for_token(
issuer: I,
snapshot: Snapshot<D, G>,
signature: N,
property: N,
token: N,
purpose: Purpose,
items: OwnedMap<N, bool, W>,
) -> Self {
let mut result = Self::new(issuer, snapshot, signature, property, purpose, items);
result.token = Some(token);
result
}
#[must_use]
pub fn subject(&self) -> N {
match self.token {
Some(token) => token,
None => self.timestamp,
}
}
pub fn selects<Q: Query<Id = N, Document = D, Generation = G>>(
&self,
node: N,
query: &Q,
) -> Result<bool, CoverageError<N>> {
if self.snapshot != query.snapshot() {
return Err(CoverageError::Foreign { node });
}
query.admit(node, 1).map_err(CoverageError::Scope)?;
self.items
.get(&node, query.execution())
.map(|value| value == Some(&true))
.map_err(|cause| CoverageError::Resource { node, cause })
}
pub fn covers<Q: Query<Id = N, Document = D, Generation = G>>(
&self,
evidence: &Verified<N, D, G, I>,
node: N,
query: &Q,
) -> Result<bool, CoverageError<N>> {
self.require_evidence(evidence, node, query)?;
self.selects(node, query)
}
pub fn require_evidence<Q: Query<Id = N, Document = D, Generation = G>>(
&self,
evidence: &Verified<N, D, G, I>,
node: N,
query: &Q,
) -> Result<(), CoverageError<N>> {
if self.snapshot != query.snapshot() {
return Err(CoverageError::Foreign { node });
}
query.admit(node, 1).map_err(CoverageError::Scope)?;
self.require_identity(
Identity {
snapshot: evidence.token().snapshot,
signature: evidence.signature(),
property: evidence.property(),
token: evidence.token().node,
purpose: evidence.purpose(),
},
node,
query.execution(),
)
}
fn require_identity<Q: Work>(
&self,
identity: Identity<N, D, G>,
node: N,
execution: &Q,
) -> Result<(), CoverageError<N>> {
let resource = |cause| CoverageError::Resource { node, cause };
if identity.snapshot != self.snapshot
|| !equal_id(identity.signature, self.signature, execution).map_err(resource)?
|| !equal_id(identity.property, self.timestamp, execution).map_err(resource)?
|| identity.purpose != self.purpose
{
return Err(CoverageError::Foreign { node });
}
if let Some(token) = self.token
&& !equal_id(identity.token, token, execution).map_err(resource)?
{
return Err(CoverageError::Foreign { node });
}
Ok(())
}
}
#[cfg(test)]
mod tests;