use super::ArtifactRef;
use crate::source::Location;
use structured_asn1::memory::{MemoryError, OwnedVec, Work};
#[derive(Clone, Copy)]
pub enum Dependency<'value, 'source, N, D, G> {
Known(ArtifactRef<'value, 'source>),
Open(Location<N, D, G>),
Missing,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Completeness {
Complete,
Unsupported,
Unprocessed,
Unusable,
Malformed,
}
#[derive(Debug)]
pub enum VisitError<E, V> {
Processing(E),
Visitor(V),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Missing {
Absent,
Uncovered,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Finding {
pub dependency: usize,
pub cause: Missing,
}
pub struct DependencyReport {
pub completeness: Completeness,
pub findings: OwnedVec<Finding>,
}
#[derive(Debug)]
pub struct DependencyRefused;
#[derive(Debug)]
pub enum DependencyError<N, E, P> {
Processing {
node: N,
cause: E,
},
Resource {
node: N,
cause: MemoryError,
},
Provider {
node: N,
cause: P,
},
CallbackProtocol {
node: N,
},
}
pub struct DependencyVisit<'w, N, D, G, W: Work, F, E> {
node: N,
work: &'w W,
resolve: F,
findings: OwnedVec<Finding, W>,
ordinal: usize,
first: Option<DependencyError<N, E, core::convert::Infallible>>,
source: core::marker::PhantomData<(D, G)>,
}
impl<'w, N: Copy, D, G, W: Work, F, E> DependencyVisit<'w, N, D, G, W, F, E>
where
F: for<'v, 's> FnMut(Dependency<'v, 's, N, D, G>) -> Result<Option<Missing>, E>,
{
pub fn new(node: N, work: &'w W, resolve: F) -> Result<Self, MemoryError> {
work.charge_work(1)?;
Ok(Self {
node,
work,
resolve,
findings: work.vector(),
ordinal: 0,
first: None,
source: core::marker::PhantomData,
})
}
pub fn observe(&mut self, need: Dependency<'_, '_, N, D, G>) -> Result<(), DependencyRefused> {
if self.first.is_some() {
return Err(DependencyRefused);
}
let resource = |cause| DependencyError::Resource {
node: self.node,
cause,
};
let result = (|| {
self.work.charge_work(1).map_err(resource)?;
let dependency = self.ordinal;
self.ordinal = dependency
.checked_add(1)
.ok_or_else(|| resource(MemoryError::WorkExceeded))?;
if let Some(cause) =
(self.resolve)(need).map_err(|cause| DependencyError::Processing {
node: self.node,
cause,
})?
{
self.findings
.try_push(Finding { dependency, cause })
.map_err(resource)?;
}
Ok(())
})();
if let Err(cause) = result {
self.first = Some(cause);
return Err(DependencyRefused);
}
Ok(())
}
pub fn finish<P>(
self,
answer: Result<Completeness, VisitError<P, DependencyRefused>>,
) -> Result<DependencyReport, DependencyError<N, E, P>> {
if let Some(cause) = self.first {
return Err(match cause {
DependencyError::Processing { node, cause } => {
DependencyError::Processing { node, cause }
}
DependencyError::Resource { node, cause } => {
DependencyError::Resource { node, cause }
}
DependencyError::CallbackProtocol { node } => {
DependencyError::CallbackProtocol { node }
}
DependencyError::Provider { cause, .. } => match cause {},
});
}
let completeness = match answer {
Ok(value) => value,
Err(VisitError::Processing(cause)) => {
return Err(DependencyError::Provider {
node: self.node,
cause,
});
}
Err(VisitError::Visitor(DependencyRefused)) => {
return Err(DependencyError::CallbackProtocol { node: self.node });
}
};
Ok(DependencyReport {
completeness,
findings: self.findings.into_owned(),
})
}
}