use crate::graph::{
DependencyDirection, FeatureIx, PackageIx,
feature::{ConditionalLink, EdgeLinks, FeatureEdgeReference},
};
use ahash::AHashMap;
use indexmap::IndexSet;
use petgraph::graph::{EdgeIndex, NodeIndex};
use smallvec::SmallVec;
#[derive(Clone, Debug)]
pub(super) struct WeakDependencies {
ixs: IndexSet<EdgeIndex<PackageIx>>,
by_optional_dependency: AHashMap<NodeIndex<FeatureIx>, SmallVec<[WeakIndex; 1]>>,
}
impl WeakDependencies {
pub(super) fn new() -> Self {
Self {
ixs: IndexSet::new(),
by_optional_dependency: AHashMap::new(),
}
}
pub(super) fn insert(
&mut self,
edge_ix: EdgeIndex<PackageIx>,
optional_dependency_ix: NodeIndex<FeatureIx>,
) -> WeakIndex {
let (index, inserted) = self.ixs.insert_full(edge_ix);
let index = WeakIndex(index);
if inserted {
self.by_optional_dependency
.entry(optional_dependency_ix)
.or_default()
.push(index);
}
index
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[doc(hidden)]
pub struct WeakIndex(pub(super) usize);
pub(super) struct WeakBufferStates<'g, 'a, F> {
buffers: WeakBuffers<'g, 'a>,
accept_fn: F,
}
enum WeakBuffers<'g, 'a> {
PerPackageEdge {
deps: &'a WeakDependencies,
states: Vec<SingleBufferState<'g>>,
},
Unbuffered,
}
impl<'g, 'a, F> WeakBufferStates<'g, 'a, F>
where
F: FnMut(ConditionalLink<'g>) -> bool,
{
#[inline]
pub(super) fn new(
deps: &'a WeakDependencies,
direction: DependencyDirection,
accept_fn: F,
) -> Self {
let buffers = match direction {
DependencyDirection::Forward => {
let len = deps.ixs.len();
let mut states = Vec::with_capacity(len);
states.resize_with(len, || SingleBufferState::Buffered(SingleBufferVec::new()));
WeakBuffers::PerPackageEdge { deps, states }
}
DependencyDirection::Reverse => WeakBuffers::Unbuffered,
};
Self { buffers, accept_fn }
}
pub(super) fn track(
&mut self,
edge_ref: FeatureEdgeReference<'g>,
links: EdgeLinks<'g>,
) -> Option<FeatureEdgeReference<'g>> {
let accepted = match links {
EdgeLinks::Weak {
required,
optional,
index,
} => {
let required_accepted = required.is_some_and(|required| (self.accept_fn)(required));
let optional_accepted = match &mut self.buffers {
WeakBuffers::PerPackageEdge { deps: _, states } => match &mut states[index.0] {
SingleBufferState::Buffered(buffer) => {
buffer.push((optional, edge_ref));
false
}
SingleBufferState::Released => (self.accept_fn)(optional),
},
WeakBuffers::Unbuffered => (self.accept_fn)(optional),
};
required_accepted || optional_accepted
}
EdgeLinks::NonWeak(link) => (self.accept_fn)(link),
};
accepted.then_some(edge_ref)
}
pub(super) fn discover(
&mut self,
feature_ix: NodeIndex<FeatureIx>,
) -> Vec<FeatureEdgeReference<'g>> {
let (deps, states) = match &mut self.buffers {
WeakBuffers::PerPackageEdge { deps, states } => (*deps, states),
WeakBuffers::Unbuffered => return Vec::new(),
};
let Some(weak_indexes) = deps.by_optional_dependency.get(&feature_ix) else {
return Vec::new();
};
release_buffers(states, weak_indexes, &mut self.accept_fn)
}
}
fn release_buffers<'g, F>(
states: &mut [SingleBufferState<'g>],
weak_indexes: &[WeakIndex],
accept_fn: &mut F,
) -> Vec<FeatureEdgeReference<'g>>
where
F: FnMut(ConditionalLink<'g>) -> bool,
{
let mut released = Vec::new();
for weak_index in weak_indexes {
match std::mem::replace(&mut states[weak_index.0], SingleBufferState::Released) {
SingleBufferState::Buffered(buffer) => {
released.extend(buffer.into_iter().filter_map(|(link, edge_ref)| {
accept_fn(link).then_some(edge_ref)
}));
}
SingleBufferState::Released => {
debug_assert!(false, "weak index {weak_index:?} is released once");
}
}
}
released
}
enum SingleBufferState<'g> {
Buffered(SingleBufferVec<'g>),
Released,
}
type SingleBufferVec<'g> = Vec<(ConditionalLink<'g>, FeatureEdgeReference<'g>)>;