use structured_asn1::memory::{HashWork, MemoryError, Work};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Snapshot<D, G> {
pub document: D,
pub generation: G,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Location<N, D, G> {
pub snapshot: Snapshot<D, G>,
pub node: N,
}
pub trait Source {
type Id: Copy + Eq + HashWork;
type Document: Copy + Eq;
type Generation: Copy + Eq;
fn snapshot(&self) -> Snapshot<Self::Document, Self::Generation>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScopeError<N> {
Changed {
node: N,
},
Resource {
node: N,
cause: MemoryError,
},
}
pub trait Query {
type Id: Copy + Eq + HashWork;
type Document: Copy + Eq;
type Generation: Copy + Eq;
type Work: Work;
fn snapshot(&self) -> Snapshot<Self::Document, Self::Generation>;
fn execution(&self) -> &Self::Work;
fn admit(&self, node: Self::Id, units: u64) -> Result<(), ScopeError<Self::Id>>;
fn equal(&self, left: Self::Id, right: Self::Id) -> Result<bool, MemoryError> {
equal_id(left, right, self.execution())
}
}
pub struct Scope<'a, S: Source, W: Work> {
source: &'a S,
snapshot: Snapshot<S::Document, S::Generation>,
execution: &'a W,
}
impl<'a, S: Source, W: Work> Scope<'a, S, W> {
#[must_use]
pub fn new(
source: &'a S,
snapshot: Snapshot<S::Document, S::Generation>,
execution: &'a W,
) -> Self {
Self {
source,
snapshot,
execution,
}
}
}
impl<S: Source, W: Work> Query for Scope<'_, S, W> {
type Id = S::Id;
type Document = S::Document;
type Generation = S::Generation;
type Work = W;
fn snapshot(&self) -> Snapshot<S::Document, S::Generation> {
self.snapshot
}
fn execution(&self) -> &W {
self.execution
}
fn admit(&self, node: S::Id, units: u64) -> Result<(), ScopeError<S::Id>> {
if self.source.snapshot() != self.snapshot {
return Err(ScopeError::Changed { node });
}
self.execution
.charge_work(units)
.map_err(|cause| ScopeError::Resource { node, cause })?;
if self.source.snapshot() != self.snapshot {
return Err(ScopeError::Changed { node });
}
Ok(())
}
}
pub fn equal_id<N: Copy + Eq + HashWork, W: Work>(
left: N,
right: N,
execution: &W,
) -> Result<bool, MemoryError> {
let cost = left
.hash_work()?
.checked_add(right.hash_work()?)
.ok_or(MemoryError::WorkExceeded)?;
execution.charge_work(cost)?;
Ok(left == right)
}