use std::{any::TypeId, fmt::Debug};
use atomic_refcell::{AtomicRef, AtomicRefMut};
use petgraph::{
Direction,
graph::NodeIndex,
visit::{Bfs, EdgeFiltered, EdgeRef},
};
use ref_cast::{RefCastCustom, ref_cast_custom};
use super::{
graph::{CookedGraph, EdgeKind, Extension, ExtensionMap, Traversal, Traverse},
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 traverse<F>(
&self,
reach: Reach,
filter: F,
) -> impl Iterator<Item = TypeView<'a>> + use<'a, Self, F>
where
F: Fn(EdgeKind, &TypeView<'a>) -> Traversal;
}
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, |e| {
matches!(cooked.graph[e.target()], GraphType::Inline(_))
});
let mut bfs = Bfs::new(&cooked.graph, self.index());
std::iter::from_fn(move || bfs.next(&filtered)).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();
let meta = &cooked.metadata.schemas[self.index().index()];
meta.used_by.iter().map(|op| OperationView::new(cooked, op))
}
#[inline]
fn dependencies(&self) -> impl Iterator<Item = TypeView<'a>> + use<'a, T> {
let cooked = self.cooked();
let meta = &cooked.metadata.schemas[self.index().index()];
meta.dependencies
.ones()
.map(NodeIndex::new)
.map(|index| TypeView::new(cooked, index))
}
#[inline]
fn dependents(&self) -> impl Iterator<Item = TypeView<'a>> + use<'a, T> {
let cooked = self.cooked();
let meta = &cooked.metadata.schemas[self.index().index()];
meta.dependents
.ones()
.map(NodeIndex::new)
.map(move |index| TypeView::new(cooked, index))
}
#[inline]
fn traverse<F>(
&self,
reach: Reach,
filter: F,
) -> impl Iterator<Item = TypeView<'a>> + use<'a, T, F>
where
F: Fn(EdgeKind, &TypeView<'a>) -> Traversal,
{
let cooked = self.cooked();
let t = Traverse::from_neighbors(
&cooked.graph,
self.index(),
match reach {
Reach::Dependencies => Direction::Outgoing,
Reach::Dependents => Direction::Incoming,
},
);
t.run(move |kind, index| {
let view = TypeView::new(cooked, index);
filter(kind, &view)
})
.map(|index| TypeView::new(cooked, 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.schemas[self.index().index()]
.extensions
.borrow()
}
#[inline]
fn ext_mut<'b>(&'b mut self) -> AtomicRefMut<'b, ExtensionMap>
where
'graph: 'b,
{
self.cooked().metadata.schemas[self.index().index()]
.extensions
.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()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Reach {
Dependencies,
Dependents,
}
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;
}
}