use std::{
cell::{Ref, RefCell},
marker::PhantomData,
rc::Rc,
};
pub use hrtb_workaround::IsDirtyInferenceWorkaround;
use crate::execution::{
error::ResolveError, next_node_id, Clean, HashValue, Identifiable, IsDirty, Named, NodeState,
Resolve, UpdateDerived, Visitor,
};
pub struct DerivedNode<D, T, F> {
dependencies: D,
value: RefCell<NodeState<T>>,
id: usize,
phantom: PhantomData<F>,
}
impl<D, T, F> DerivedNode<D, T, F>
where
for<'a> D: Resolve + IsDirtyInferenceWorkaround<'a> + 'a,
for<'a> T: UpdateDerived<<D as Resolve>::Output<'a>, F> + 'a,
T: HashValue + Clean + Named,
F: Named,
{
pub fn new(dependencies: D, operation: F, value: T) -> Rc<Self> {
Self::new_with_id(dependencies, operation, value, next_node_id())
}
pub fn new_with_id(dependencies: D, _: F, value: T, id: usize) -> Rc<Self> {
Rc::new(Self {
dependencies,
value: RefCell::new(NodeState::new(value)),
id,
phantom: PhantomData,
})
}
}
impl<D, T, F> Resolve for DerivedNode<D, T, F>
where
for<'a> D: Resolve + IsDirtyInferenceWorkaround<'a> + 'a,
for<'a> T: UpdateDerived<<D as IsDirtyInferenceWorkaround<'a>>::OutputWorkaround, F>,
T: HashValue + Clean + Named,
F: Named,
{
type Output<'a>
= Ref<'a, NodeState<T>>
where
Self: 'a;
fn resolve(&self, visitor: &mut impl Visitor) -> Result<Self::Output<'_>, ResolveError> {
visitor.touch(self, Some(F::name()));
if visitor.visit(self) {
let mut node_state = self.value.try_borrow_mut()?;
node_state.clean();
let input = self.dependencies.resolve_workaround(visitor)?;
if input.is_dirty() {
node_state.value_mut().update(input)?;
drop(node_state);
let mut node_state = self.value.try_borrow_mut()?;
node_state.update_node_hash(&mut visitor.hasher());
visitor.notify_recalculated(self);
}
}
visitor.leave(self);
Ok(self.value.try_borrow()?)
}
}
impl<D, T: Named, F> Named for DerivedNode<D, T, F> {
fn name() -> &'static str {
T::name()
}
}
impl<D, T: Named, F> Identifiable for DerivedNode<D, T, F> {
fn id(&self) -> usize {
self.id
}
}
mod hrtb_workaround {
use super::*;
pub trait IsDirtyInferenceWorkaround<'a>: Resolve + 'a {
type OutputWorkaround: IsDirty;
fn resolve_workaround(
&'a self,
visitor: &'a mut impl Visitor,
) -> Result<Self::OutputWorkaround, ResolveError>;
}
impl<'a, T> IsDirtyInferenceWorkaround<'a> for T
where
T: Resolve + 'a,
<T as Resolve>::Output<'a>: IsDirty,
{
type OutputWorkaround = <T as Resolve>::Output<'a>;
fn resolve_workaround(
&'a self,
visitor: &'a mut impl Visitor,
) -> Result<Self::OutputWorkaround, ResolveError> {
self.resolve(visitor)
}
}
}