use ahash::{HashMap, HashSet};
use educe::Educe;
use std::iter::FusedIterator;
use crate::resource::id::ResourceId;
#[derive(Educe, Default, Clone, PartialEq, Eq)]
#[educe(Debug)]
pub struct DependencyGraph {
dependencies: HashMap<ResourceId, HashSet<ResourceId>>,
#[educe(Debug(ignore))]
dependents: HashMap<ResourceId, HashSet<ResourceId>>,
}
impl DependencyGraph {
#[inline]
pub fn builder() -> DependencyGraphBuilder {
DependencyGraphBuilder::default()
}
pub fn clear_dependencies(&mut self, id: &ResourceId) {
if let Some(dependencies) = self.dependencies.remove(id) {
for dependency in dependencies {
let mut remove = false;
if let Some(dependents) = self.dependents.get_mut(&dependency) {
dependents.remove(id);
if dependents.is_empty() {
remove = true;
}
}
if remove {
self.dependents.remove(&dependency);
}
}
}
}
pub fn add_dependency(&mut self, id: ResourceId, dependency: ResourceId) {
self.dependencies.entry(id.clone())
.or_default()
.insert(dependency.clone());
self.dependents.entry(dependency)
.or_default()
.insert(id);
}
pub fn set_dependencies(&mut self, id: ResourceId, dependencies: impl IntoIterator<Item=ResourceId>) {
self.clear_dependencies(&id);
let dependencies = dependencies.into_iter().collect::<HashSet<ResourceId>>();
if !dependencies.is_empty() {
for dependency in &dependencies {
self.dependents.entry(dependency.clone())
.or_default()
.insert(id.clone());
}
self.dependencies.insert(id, dependencies);
}
}
pub fn get(&self, id: &ResourceId) -> Option<DependencyNode<'_>> {
if let Some((id, _)) = self.dependencies.get_key_value(id) {
Some(DependencyNode {
graph: self,
id,
})
} else if let Some((id, _)) = self.dependents.get_key_value(id) {
Some(DependencyNode {
graph: self,
id,
})
} else {
None
}
}
#[inline]
pub fn iter(&self) -> Iter<'_> {
Iter {
graph: self,
inner: self.dependencies.keys(),
}
}
}
#[derive(Debug, Clone)]
pub struct Iter<'a> {
graph: &'a DependencyGraph,
inner: std::collections::hash_map::Keys<'a, ResourceId, HashSet<ResourceId>>,
}
impl<'a> Iterator for Iter<'a> {
type Item = DependencyNode<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|id| DependencyNode {
graph: self.graph,
id,
})
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<'a> ExactSizeIterator for Iter<'a> {}
impl<'a> FusedIterator for Iter<'a> {}
#[derive(Educe, Clone, Copy)]
#[educe(Debug)]
pub struct DependencyNode<'a> {
#[educe(Debug(ignore))]
graph: &'a DependencyGraph,
id: &'a ResourceId,
}
impl<'a> PartialEq for DependencyNode<'a> {
fn eq(&self, other: &Self) -> bool {
if !std::ptr::eq(self.graph, other.graph) {
return false
}
self.id == other.id
}
}
impl<'a> Eq for DependencyNode<'a> {}
impl<'a> DependencyNode<'a> {
#[inline]
pub fn id(&self) -> &'a ResourceId {
self.id
}
#[inline]
pub fn is_root(&self) -> bool {
if let Some(dependents) = self.graph.dependents.get(self.id) {
dependents.is_empty()
} else {
true
}
}
#[inline]
pub fn is_leaf(&self) -> bool {
if let Some(dependencies) = self.graph.dependencies.get(self.id) {
dependencies.is_empty()
} else {
true
}
}
#[inline]
pub fn dependencies(&self) -> NodeIter<'a> {
if let Some(dependencies) = self.graph.dependencies.get(self.id) {
NodeIter {
graph: self.graph,
inner: dependencies.iter(),
direction: NodeIterDirection::Dependencies,
}
} else {
NodeIter {
graph: self.graph,
inner: Default::default(),
direction: NodeIterDirection::Dependencies,
}
}
}
#[inline]
pub fn dependents(&self) -> NodeIter<'a> {
if let Some(dependents) = self.graph.dependents.get(self.id) {
NodeIter {
graph: self.graph,
inner: dependents.iter(),
direction: NodeIterDirection::Dependents,
}
} else {
NodeIter {
graph: self.graph,
inner: Default::default(),
direction: NodeIterDirection::Dependents,
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum NodeIterDirection {
Dependencies,
Dependents,
}
#[derive(Debug, Clone)]
pub struct NodeIter<'a> {
graph: &'a DependencyGraph,
inner: std::collections::hash_set::Iter<'a, ResourceId>,
direction: NodeIterDirection,
}
impl<'a> NodeIter<'a> {
pub fn recursive(self) -> NodeRecursiveIter<'a> {
NodeRecursiveIter {
nodes: self,
next_layer: None,
}
}
}
impl<'a> Iterator for NodeIter<'a> {
type Item = DependencyNode<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|id| DependencyNode {
graph: self.graph,
id,
})
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<'a> ExactSizeIterator for NodeIter<'a> {}
impl<'a> FusedIterator for NodeIter<'a> {}
pub struct NodeRecursiveIter<'a> {
nodes: NodeIter<'a>,
next_layer: Option<Box<Self>>,
}
impl<'a> Iterator for NodeRecursiveIter<'a> {
type Item = DependencyNode<'a>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(next_layer) = &mut self.next_layer {
if let Some(node) = next_layer.next() {
return Some(node)
} else {
self.next_layer = None;
}
}
if let Some(node) = self.nodes.next() {
self.next_layer = Some(Box::new(match self.nodes.direction {
NodeIterDirection::Dependencies => node.dependencies().recursive(),
NodeIterDirection::Dependents => node.dependents().recursive(),
}));
Some(node)
} else {
None
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.nodes.len(), None)
}
}
impl<'a> FusedIterator for NodeRecursiveIter<'a> {}
#[derive(Default)]
pub struct DependencyGraphBuilder {
graph: DependencyGraph,
}
impl DependencyGraphBuilder {
#[inline]
pub fn add(
&mut self,
id: impl Into<ResourceId>,
dependencies: impl IntoIterator<Item=impl Into<ResourceId>>,
) -> &mut Self {
self.graph.set_dependencies(
id.into(),
dependencies.into_iter().map(Into::into),
);
self
}
#[inline]
pub fn build(&self) -> DependencyGraph {
self.graph.clone()
}
}
pub(in crate::resource) struct DependencyGraphLayer {
parents: Vec<ResourceId>,
id: ResourceId,
dependencies: HashSet<ResourceId>,
}
impl DependencyGraphLayer {
#[inline]
pub fn new(id: ResourceId, parents: impl IntoIterator<Item=ResourceId>) -> Self {
Self {
parents: parents.into_iter().collect(),
id,
dependencies: Default::default(),
}
}
#[inline]
pub fn parents(&self) -> std::slice::Iter<'_, ResourceId> {
self.parents.iter()
}
#[inline]
pub fn id(&self) -> &ResourceId {
&self.id
}
#[inline]
pub fn insert(&mut self, dependency: ResourceId) {
self.dependencies.insert(dependency);
}
#[inline]
pub fn apply(self, graph: &mut DependencyGraph) {
graph.set_dependencies(self.id, self.dependencies);
}
}