use std::{any::TypeId, fmt::Debug};
use atomic_refcell::{AtomicRef, AtomicRefMut};
use petgraph::{
graph::NodeIndex,
visit::{Bfs, EdgeFiltered, EdgeRef},
};
use ref_cast::{RefCastCustom, ref_cast_custom};
use super::{
graph::{CookedGraph, Extension, ExtensionMap},
types::GraphType,
};
pub mod any;
pub mod container;
pub mod enum_;
pub mod inline;
pub mod ir;
pub mod operation;
pub mod primitive;
pub mod schema;
pub mod struct_;
pub mod tagged;
pub mod untagged;
use self::{inline::InlineTypeView, ir::TypeView, operation::OperationView};
pub trait View<'a> {
fn inlines(&self) -> impl Iterator<Item = InlineTypeView<'a>> + use<'a, Self>;
fn used_by(&self) -> impl Iterator<Item = OperationView<'a>> + use<'a, Self>;
fn dependencies(&self) -> impl Iterator<Item = TypeView<'a>> + use<'a, Self>;
fn dependents(&self) -> impl Iterator<Item = TypeView<'a>> + use<'a, Self>;
fn hashable(&self) -> bool;
fn defaultable(&self) -> bool;
}
pub trait ExtendableView<'a>: View<'a> {
fn extensions(&self) -> &ViewExtensions<Self>
where
Self: Sized;
fn extensions_mut(&mut self) -> &mut ViewExtensions<Self>
where
Self: Sized;
}
impl<'a, T> View<'a> for T
where
T: ViewNode<'a>,
{
#[inline]
fn inlines(&self) -> impl Iterator<Item = InlineTypeView<'a>> + use<'a, T> {
let cooked = self.cooked();
let filtered = EdgeFiltered::from_fn(&cooked.graph, move |e| {
!e.weight().shadow() && matches!(cooked.graph[e.target()], GraphType::Inline(_))
});
let mut bfs = Bfs::new(&cooked.graph, self.index());
std::iter::from_fn(move || bfs.next(&filtered))
.skip(1) .filter_map(|index| match cooked.graph[index] {
GraphType::Inline(ty) => Some(InlineTypeView::new(cooked, index, ty)),
_ => None,
})
}
#[inline]
fn used_by(&self) -> impl Iterator<Item = OperationView<'a>> + use<'a, T> {
let cooked = self.cooked();
cooked.metadata.used_by[self.index().index()]
.iter()
.map(|op| OperationView::new(cooked, op))
}
#[inline]
fn dependencies(&self) -> impl Iterator<Item = TypeView<'a>> + use<'a, T> {
let cooked = self.cooked();
let start = self.index();
cooked
.metadata
.closure
.dependencies_of(start)
.filter(move |&index| index != start)
.map(|index| TypeView::new(cooked, index))
}
#[inline]
fn dependents(&self) -> impl Iterator<Item = TypeView<'a>> + use<'a, T> {
let cooked = self.cooked();
let start = self.index();
cooked
.metadata
.closure
.dependents_of(start)
.filter(move |&index| index != start)
.map(|index| TypeView::new(cooked, index))
}
#[inline]
fn hashable(&self) -> bool {
self.cooked().metadata.hashable[self.index().index()]
}
#[inline]
fn defaultable(&self) -> bool {
self.cooked().metadata.defaultable[self.index().index()]
}
}
impl<'a, T> ExtendableView<'a> for T
where
T: ViewNode<'a>,
{
#[inline]
fn extensions(&self) -> &ViewExtensions<Self> {
ViewExtensions::new(self)
}
#[inline]
fn extensions_mut(&mut self) -> &mut ViewExtensions<Self> {
ViewExtensions::new_mut(self)
}
}
pub(crate) trait ViewNode<'a> {
fn cooked(&self) -> &'a CookedGraph<'a>;
fn index(&self) -> NodeIndex<usize>;
}
impl<'graph, T> internal::Extendable<'graph> for T
where
T: ViewNode<'graph>,
{
#[inline]
fn ext<'view>(&'view self) -> AtomicRef<'view, ExtensionMap>
where
'graph: 'view,
{
self.cooked().metadata.extensions[self.index().index()].borrow()
}
#[inline]
fn ext_mut<'b>(&'b mut self) -> AtomicRefMut<'b, ExtensionMap>
where
'graph: 'b,
{
self.cooked().metadata.extensions[self.index().index()].borrow_mut()
}
}
#[derive(RefCastCustom)]
#[repr(transparent)]
pub struct ViewExtensions<X>(X);
impl<X> ViewExtensions<X> {
#[ref_cast_custom]
fn new(view: &X) -> &Self;
#[ref_cast_custom]
fn new_mut(view: &mut X) -> &mut Self;
}
impl<'a, X: internal::Extendable<'a>> ViewExtensions<X> {
#[inline]
pub fn get<'b, T: Send + Sync + 'static>(&'b self) -> Option<AtomicRef<'b, T>>
where
'a: 'b,
{
AtomicRef::filter_map(self.0.ext(), |ext| {
Some(
ext.get(&TypeId::of::<T>())?
.as_ref()
.downcast_ref::<T>()
.unwrap(),
)
})
}
#[inline]
pub fn insert<T: Send + Sync + 'static>(&mut self, value: T) -> Option<T> {
self.0
.ext_mut()
.insert(TypeId::of::<T>(), Box::new(value))
.and_then(|old| *Extension::into_inner(old).downcast().unwrap())
}
}
impl<X> Debug for ViewExtensions<X> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ViewExtensions").finish_non_exhaustive()
}
}
mod internal {
use atomic_refcell::{AtomicRef, AtomicRefMut};
use super::ExtensionMap;
pub trait Extendable<'graph> {
fn ext<'view>(&'view self) -> AtomicRef<'view, ExtensionMap>
where
'graph: 'view;
fn ext_mut<'view>(&'view mut self) -> AtomicRefMut<'view, ExtensionMap>
where
'graph: 'view;
}
}