use std::any::Any;
use std::marker::PhantomData;
use std::ops::Deref;
use crate::context::{inject, ContextKey, ContextMarker};
use crate::handle::Handle;
use crate::lifecycle::LifecycleContext;
use crate::reactive::{track, ScopeId};
use crate::scope::{with_current_scope_id, Scope};
use crate::watch::watch;
const PARENT_OBSERVE_KEY: &str = "__pp_parent_observe";
pub struct Parent<T: 'static> {
handle: Handle<T>,
}
impl<T: 'static> Parent<T> {
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
self.handle.with(f)
}
pub fn update<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
self.handle.update(f)
}
pub fn observe<V>(
&self,
selector: impl Fn(&T) -> V + 'static,
cb: impl Fn(V, Option<V>) + 'static,
) where
V: Clone + PartialEq + 'static,
{
let parent_scope = self.handle.scope_id();
let handle = self.handle.clone();
crate::tick::next(move || {
watch(
move || {
track(parent_scope, PARENT_OBSERVE_KEY);
handle.with(|s| selector(s))
},
move |new, prev| cb(new.clone(), prev.cloned()),
);
});
}
pub fn handle(&self) -> Handle<T> {
self.handle.clone()
}
}
impl<T: 'static> Clone for Parent<T> {
fn clone(&self) -> Self {
Self {
handle: self.handle.clone(),
}
}
}
pub struct NearestParent<T: 'static> {
handle: Handle<T>,
}
impl<T: 'static> NearestParent<T> {
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
self.handle.with(f)
}
pub fn update<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
self.handle.update(f)
}
pub fn observe<V>(
&self,
selector: impl Fn(&T) -> V + 'static,
cb: impl Fn(V, Option<V>) + 'static,
) where
V: Clone + PartialEq + 'static,
{
let parent_scope = self.handle.scope_id();
let handle = self.handle.clone();
crate::tick::next(move || {
watch(
move || {
track(parent_scope, PARENT_OBSERVE_KEY);
handle.with(|s| selector(s))
},
move |new, prev| cb(new.clone(), prev.cloned()),
);
});
}
pub fn handle(&self) -> Handle<T> {
self.handle.clone()
}
}
impl<T: 'static> Clone for NearestParent<T> {
fn clone(&self) -> Self {
Self {
handle: self.handle.clone(),
}
}
}
fn resolve_immediate_parent<T: 'static>(child: ScopeId) -> Option<Handle<T>> {
let parent_id = crate::context::parent_of(child)?;
let scope = Scope::find(parent_id)?;
let rc = scope.typed::<T>()?;
Some(Handle::new(rc, parent_id))
}
fn resolve_nearest_ancestor<T: 'static>(child: ScopeId) -> Option<Handle<T>> {
let mut cur = child;
while let Some(parent_id) = crate::context::parent_of(cur) {
if let Some(scope) = Scope::find(parent_id) {
if let Some(rc) = scope.typed::<T>() {
return Some(Handle::new(rc, parent_id));
}
}
cur = parent_id;
}
None
}
impl<'a, T: 'static> From<LifecycleContext<'a>> for Parent<T> {
fn from(ctx: LifecycleContext<'a>) -> Self {
let handle = resolve_immediate_parent::<T>(ctx.scope_id).unwrap_or_else(|| {
panic!(
"Parent<{}>: immediate parent scope is not of this type",
std::any::type_name::<T>()
)
});
Parent { handle }
}
}
impl<'a, T: 'static> From<LifecycleContext<'a>> for Option<Parent<T>> {
fn from(ctx: LifecycleContext<'a>) -> Self {
resolve_immediate_parent::<T>(ctx.scope_id).map(|handle| Parent { handle })
}
}
impl<'a, T: 'static> From<LifecycleContext<'a>> for NearestParent<T> {
fn from(ctx: LifecycleContext<'a>) -> Self {
let handle = resolve_nearest_ancestor::<T>(ctx.scope_id).unwrap_or_else(|| {
panic!(
"NearestParent<{}>: no ancestor scope of this type",
std::any::type_name::<T>()
)
});
NearestParent { handle }
}
}
impl<'a, T: 'static> From<LifecycleContext<'a>> for Option<NearestParent<T>> {
fn from(ctx: LifecycleContext<'a>) -> Self {
resolve_nearest_ancestor::<T>(ctx.scope_id).map(|handle| NearestParent { handle })
}
}
pub struct Inject<KEY, T>
where
KEY: ContextMarker<Value = T>,
T: Clone + Any + 'static,
{
value: T,
_key: PhantomData<fn() -> KEY>,
}
impl<KEY, T> Inject<KEY, T>
where
KEY: ContextMarker<Value = T>,
T: Clone + Any + 'static,
{
pub fn into_inner(self) -> T {
self.value
}
}
impl<KEY, T> Deref for Inject<KEY, T>
where
KEY: ContextMarker<Value = T>,
T: Clone + Any + 'static,
{
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
impl<KEY, T> Clone for Inject<KEY, T>
where
KEY: ContextMarker<Value = T>,
T: Clone + Any + 'static,
{
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
_key: PhantomData,
}
}
}
fn resolve_inject<T: Clone + Any + 'static>(start: ScopeId, key: &ContextKey<T>) -> Option<T> {
with_current_scope_id(start, || inject(key))
}
impl<'a, KEY, T> From<LifecycleContext<'a>> for Inject<KEY, T>
where
KEY: ContextMarker<Value = T>,
T: Clone + Any + 'static,
{
fn from(ctx: LifecycleContext<'a>) -> Self {
let value = resolve_inject(ctx.scope_id, KEY::key()).unwrap_or_else(|| {
panic!(
"Inject<{}, {}>: key not provided by any ancestor",
std::any::type_name::<KEY>(),
std::any::type_name::<T>()
)
});
Inject {
value,
_key: PhantomData,
}
}
}
impl<'a, KEY, T> From<LifecycleContext<'a>> for Option<Inject<KEY, T>>
where
KEY: ContextMarker<Value = T>,
T: Clone + Any + 'static,
{
fn from(ctx: LifecycleContext<'a>) -> Self {
resolve_inject(ctx.scope_id, KEY::key()).map(|value| Inject {
value,
_key: PhantomData,
})
}
}