use std::marker::PhantomData;
use std::rc::Rc;
use crate::{CellHandle, Context, SlotHandle};
#[cfg(feature = "thread-safe")]
use crate::ThreadSafeContext;
pub struct TypedContext<Schema> {
inner: Context,
_schema: PhantomData<fn() -> Schema>,
}
pub struct TypedContextRef<'a, Schema> {
inner: &'a Context,
_schema: PhantomData<fn() -> Schema>,
}
pub struct TypedSlotHandle<Schema, T> {
raw: SlotHandle<T>,
_schema: PhantomData<fn() -> Schema>,
}
pub struct TypedCellHandle<Schema, T> {
raw: CellHandle<T>,
_schema: PhantomData<fn() -> Schema>,
}
pub trait TypedGet<Schema, Kind> {
type Output;
fn get_typed(self, ctx: &TypedContext<Schema>) -> Self::Output;
}
pub trait TypedGetRef<Schema, Kind> {
type Output;
fn get_typed_ref(self, ctx: &TypedContextRef<'_, Schema>) -> Self::Output;
}
pub trait TypedSet<Schema, T, Kind> {
fn set_typed(self, ctx: &TypedContext<Schema>, value: T);
}
pub trait TypedContextFamily {
type Schema: 'static;
}
pub struct TypedSlotHandleSource;
pub struct TypedCellHandleSource;
pub struct TypedSlotFactorySource;
pub struct TypedCellFactorySource;
pub trait TypedFactoryContext {
type Schema: 'static;
fn memoized_slot<K, T, F>(&self, compute: F) -> TypedSlotHandle<Self::Schema, T>
where
K: 'static,
T: 'static,
F: for<'a> Fn(&TypedContextRef<'a, Self::Schema>) -> T + 'static;
fn memoized_cell<K, T, F>(&self, init: F) -> TypedCellHandle<Self::Schema, T>
where
K: 'static,
T: PartialEq + 'static,
F: for<'a> FnOnce(&TypedContextRef<'a, Self::Schema>) -> T;
}
#[cfg(feature = "thread-safe")]
pub struct TypedThreadSafeContext<Schema> {
inner: ThreadSafeContext,
_schema: PhantomData<fn() -> Schema>,
}
#[cfg(feature = "thread-safe")]
pub struct TypedThreadSafeContextRef<'a, Schema> {
inner: &'a ThreadSafeContext,
_schema: PhantomData<fn() -> Schema>,
}
#[cfg(feature = "thread-safe")]
pub struct TypedThreadSafeSlotHandle<Schema, T> {
raw: SlotHandle<T>,
_schema: PhantomData<fn() -> Schema>,
}
#[cfg(feature = "thread-safe")]
pub struct TypedThreadSafeCellHandle<Schema, T> {
raw: CellHandle<T>,
_schema: PhantomData<fn() -> Schema>,
}
impl<Schema> TypedContext<Schema> {
pub fn new() -> Self {
Self {
inner: Context::new(),
_schema: PhantomData,
}
}
pub fn raw(&self) -> &Context {
&self.inner
}
pub fn as_ref(&self) -> TypedContextRef<'_, Schema> {
TypedContextRef::new(&self.inner)
}
pub fn cell<T>(&self, value: T) -> TypedCellHandle<Schema, T>
where
T: PartialEq + 'static,
{
TypedCellHandle::new(self.inner.cell(value))
}
pub fn slot<T, F>(&self, compute: F) -> TypedSlotHandle<Schema, T>
where
T: 'static,
F: for<'a> Fn(&TypedContextRef<'a, Schema>) -> T + 'static,
{
let raw = self.inner.slot(move |ctx| {
let typed = TypedContextRef::new(ctx);
compute(&typed)
});
TypedSlotHandle::new(raw)
}
pub fn computed<T, F>(&self, compute: F) -> TypedSlotHandle<Schema, T>
where
T: 'static,
F: for<'a> Fn(&TypedContextRef<'a, Schema>) -> T + 'static,
{
self.slot(compute)
}
pub fn memo<T, F>(&self, compute: F) -> TypedSlotHandle<Schema, T>
where
T: PartialEq + 'static,
F: for<'a> Fn(&TypedContextRef<'a, Schema>) -> T + 'static,
{
let raw = self.inner.memo(move |ctx| {
let typed = TypedContextRef::new(ctx);
compute(&typed)
});
TypedSlotHandle::new(raw)
}
pub fn memoized_slot<K, T, F>(&self, compute: F) -> TypedSlotHandle<Schema, T>
where
K: 'static,
T: 'static,
F: for<'a> Fn(&TypedContextRef<'a, Schema>) -> T + 'static,
Schema: 'static,
{
<Self as TypedFactoryContext>::memoized_slot::<K, T, F>(self, compute)
}
pub fn memoized_cell<K, T, F>(&self, init: F) -> TypedCellHandle<Schema, T>
where
K: 'static,
T: PartialEq + 'static,
F: for<'a> FnOnce(&TypedContextRef<'a, Schema>) -> T,
Schema: 'static,
{
<Self as TypedFactoryContext>::memoized_cell::<K, T, F>(self, init)
}
pub fn get<S, Kind>(&self, source: S) -> S::Output
where
S: TypedGet<Schema, Kind>,
{
source.get_typed(self)
}
pub fn get_rc<T>(&self, handle: &TypedSlotHandle<Schema, T>) -> Rc<T>
where
T: 'static,
{
self.inner.get_rc(&handle.raw)
}
pub fn get_cell<T>(&self, handle: &TypedCellHandle<Schema, T>) -> T
where
T: Clone + 'static,
{
self.inner.get_cell(&handle.raw)
}
pub fn get_cell_rc<T>(&self, handle: &TypedCellHandle<Schema, T>) -> Rc<T>
where
T: 'static,
{
self.inner.get_cell_rc(&handle.raw)
}
pub fn set_cell<T>(&self, handle: &TypedCellHandle<Schema, T>, value: T)
where
T: PartialEq + 'static,
{
self.inner.set_cell(&handle.raw, value);
}
pub fn set<S, T, Kind>(&self, source: S, value: T)
where
S: TypedSet<Schema, T, Kind>,
{
source.set_typed(self, value);
}
pub fn is_set<T>(&self, handle: &TypedSlotHandle<Schema, T>) -> bool
where
T: 'static,
{
self.inner.is_set(&handle.raw)
}
}
impl<Schema> Default for TypedContext<Schema> {
fn default() -> Self {
Self::new()
}
}
impl<Schema: 'static> TypedContextFamily for TypedContext<Schema> {
type Schema = Schema;
}
impl<Schema: 'static> TypedFactoryContext for TypedContext<Schema> {
type Schema = Schema;
fn memoized_slot<K, T, F>(&self, compute: F) -> TypedSlotHandle<Self::Schema, T>
where
K: 'static,
T: 'static,
F: for<'a> Fn(&TypedContextRef<'a, Self::Schema>) -> T + 'static,
{
let raw = self.inner.memoized_slot::<K, T, _>(move |ctx| {
let typed = TypedContextRef::new(ctx);
compute(&typed)
});
TypedSlotHandle::new(raw)
}
fn memoized_cell<K, T, F>(&self, init: F) -> TypedCellHandle<Self::Schema, T>
where
K: 'static,
T: PartialEq + 'static,
F: for<'a> FnOnce(&TypedContextRef<'a, Self::Schema>) -> T,
{
let raw = self.inner.memoized_cell::<K, T, _>(move |ctx| {
let typed = TypedContextRef::new(ctx);
init(&typed)
});
TypedCellHandle::new(raw)
}
}
impl<'a, Schema> TypedContextRef<'a, Schema> {
fn new(inner: &'a Context) -> Self {
Self {
inner,
_schema: PhantomData,
}
}
pub fn raw(&self) -> &'a Context {
self.inner
}
pub fn get<S, Kind>(&self, source: S) -> S::Output
where
S: TypedGetRef<Schema, Kind>,
{
source.get_typed_ref(self)
}
pub fn get_rc<T>(&self, handle: &TypedSlotHandle<Schema, T>) -> Rc<T>
where
T: 'static,
{
self.inner.get_rc(&handle.raw)
}
pub fn get_cell<T>(&self, handle: &TypedCellHandle<Schema, T>) -> T
where
T: Clone + 'static,
{
self.inner.get_cell(&handle.raw)
}
pub fn get_cell_rc<T>(&self, handle: &TypedCellHandle<Schema, T>) -> Rc<T>
where
T: 'static,
{
self.inner.get_cell_rc(&handle.raw)
}
pub fn memoized_slot<K, T, F>(&self, compute: F) -> TypedSlotHandle<Schema, T>
where
K: 'static,
T: 'static,
F: for<'b> Fn(&TypedContextRef<'b, Schema>) -> T + 'static,
Schema: 'static,
{
<Self as TypedFactoryContext>::memoized_slot::<K, T, F>(self, compute)
}
pub fn memoized_cell<K, T, F>(&self, init: F) -> TypedCellHandle<Schema, T>
where
K: 'static,
T: PartialEq + 'static,
F: for<'b> FnOnce(&TypedContextRef<'b, Schema>) -> T,
Schema: 'static,
{
<Self as TypedFactoryContext>::memoized_cell::<K, T, F>(self, init)
}
}
impl<Schema: 'static> TypedContextFamily for TypedContextRef<'_, Schema> {
type Schema = Schema;
}
impl<Schema: 'static> TypedFactoryContext for TypedContextRef<'_, Schema> {
type Schema = Schema;
fn memoized_slot<K, T, F>(&self, compute: F) -> TypedSlotHandle<Self::Schema, T>
where
K: 'static,
T: 'static,
F: for<'a> Fn(&TypedContextRef<'a, Self::Schema>) -> T + 'static,
{
let raw = self.inner.memoized_slot::<K, T, _>(move |ctx| {
let typed = TypedContextRef::new(ctx);
compute(&typed)
});
TypedSlotHandle::new(raw)
}
fn memoized_cell<K, T, F>(&self, init: F) -> TypedCellHandle<Self::Schema, T>
where
K: 'static,
T: PartialEq + 'static,
F: for<'a> FnOnce(&TypedContextRef<'a, Self::Schema>) -> T,
{
let raw = self.inner.memoized_cell::<K, T, _>(move |ctx| {
let typed = TypedContextRef::new(ctx);
init(&typed)
});
TypedCellHandle::new(raw)
}
}
impl<Schema, T> TypedGet<Schema, TypedSlotHandleSource> for &TypedSlotHandle<Schema, T>
where
T: Clone + 'static,
{
type Output = T;
fn get_typed(self, ctx: &TypedContext<Schema>) -> Self::Output {
ctx.inner.get(&self.raw)
}
}
impl<Schema, T> TypedGet<Schema, TypedSlotHandleSource> for TypedSlotHandle<Schema, T>
where
T: Clone + 'static,
{
type Output = T;
fn get_typed(self, ctx: &TypedContext<Schema>) -> Self::Output {
ctx.inner.get(&self.raw)
}
}
impl<Schema, T> TypedGetRef<Schema, TypedSlotHandleSource> for &TypedSlotHandle<Schema, T>
where
T: Clone + 'static,
{
type Output = T;
fn get_typed_ref(self, ctx: &TypedContextRef<'_, Schema>) -> Self::Output {
ctx.inner.get(&self.raw)
}
}
impl<Schema, T> TypedGetRef<Schema, TypedSlotHandleSource> for TypedSlotHandle<Schema, T>
where
T: Clone + 'static,
{
type Output = T;
fn get_typed_ref(self, ctx: &TypedContextRef<'_, Schema>) -> Self::Output {
ctx.inner.get(&self.raw)
}
}
impl<Schema, T> TypedGet<Schema, TypedCellHandleSource> for &TypedCellHandle<Schema, T>
where
T: Clone + 'static,
{
type Output = T;
fn get_typed(self, ctx: &TypedContext<Schema>) -> Self::Output {
ctx.inner.get_cell(&self.raw)
}
}
impl<Schema, T> TypedGet<Schema, TypedCellHandleSource> for TypedCellHandle<Schema, T>
where
T: Clone + 'static,
{
type Output = T;
fn get_typed(self, ctx: &TypedContext<Schema>) -> Self::Output {
ctx.inner.get_cell(&self.raw)
}
}
impl<Schema, T> TypedGetRef<Schema, TypedCellHandleSource> for &TypedCellHandle<Schema, T>
where
T: Clone + 'static,
{
type Output = T;
fn get_typed_ref(self, ctx: &TypedContextRef<'_, Schema>) -> Self::Output {
ctx.inner.get_cell(&self.raw)
}
}
impl<Schema, T> TypedGetRef<Schema, TypedCellHandleSource> for TypedCellHandle<Schema, T>
where
T: Clone + 'static,
{
type Output = T;
fn get_typed_ref(self, ctx: &TypedContextRef<'_, Schema>) -> Self::Output {
ctx.inner.get_cell(&self.raw)
}
}
impl<Schema: 'static, T, F> TypedGet<Schema, TypedSlotFactorySource> for F
where
T: Clone + 'static,
F: Fn(&TypedContext<Schema>) -> TypedSlotHandle<Schema, T>,
{
type Output = T;
fn get_typed(self, ctx: &TypedContext<Schema>) -> Self::Output {
let handle = self(ctx);
ctx.inner.get(&handle.raw)
}
}
impl<Schema: 'static, T, F> TypedGetRef<Schema, TypedSlotFactorySource> for F
where
T: Clone + 'static,
F: for<'a> Fn(&TypedContextRef<'a, Schema>) -> TypedSlotHandle<Schema, T>,
{
type Output = T;
fn get_typed_ref(self, ctx: &TypedContextRef<'_, Schema>) -> Self::Output {
let handle = self(ctx);
ctx.inner.get(&handle.raw)
}
}
impl<Schema: 'static, T, F> TypedGet<Schema, TypedCellFactorySource> for F
where
T: Clone + 'static,
F: Fn(&TypedContext<Schema>) -> TypedCellHandle<Schema, T>,
{
type Output = T;
fn get_typed(self, ctx: &TypedContext<Schema>) -> Self::Output {
let handle = self(ctx);
ctx.inner.get_cell(&handle.raw)
}
}
impl<Schema: 'static, T, F> TypedGetRef<Schema, TypedCellFactorySource> for F
where
T: Clone + 'static,
F: for<'a> Fn(&TypedContextRef<'a, Schema>) -> TypedCellHandle<Schema, T>,
{
type Output = T;
fn get_typed_ref(self, ctx: &TypedContextRef<'_, Schema>) -> Self::Output {
let handle = self(ctx);
ctx.inner.get_cell(&handle.raw)
}
}
impl<Schema, T> TypedSet<Schema, T, TypedCellHandleSource> for &TypedCellHandle<Schema, T>
where
T: PartialEq + 'static,
{
fn set_typed(self, ctx: &TypedContext<Schema>, value: T) {
ctx.inner.set_cell(&self.raw, value);
}
}
impl<Schema, T> TypedSet<Schema, T, TypedCellHandleSource> for TypedCellHandle<Schema, T>
where
T: PartialEq + 'static,
{
fn set_typed(self, ctx: &TypedContext<Schema>, value: T) {
ctx.inner.set_cell(&self.raw, value);
}
}
impl<Schema: 'static, T, F> TypedSet<Schema, T, TypedCellFactorySource> for F
where
T: PartialEq + 'static,
F: Fn(&TypedContext<Schema>) -> TypedCellHandle<Schema, T>,
{
fn set_typed(self, ctx: &TypedContext<Schema>, value: T) {
let handle = self(ctx);
ctx.inner.set_cell(&handle.raw, value);
}
}
impl<Schema, T> TypedSlotHandle<Schema, T> {
fn new(raw: SlotHandle<T>) -> Self {
Self {
raw,
_schema: PhantomData,
}
}
pub fn raw(&self) -> SlotHandle<T> {
self.raw
}
pub fn get(&self, ctx: &TypedContext<Schema>) -> T
where
T: Clone + 'static,
{
ctx.get(self)
}
pub fn get_ref(&self, ctx: &TypedContextRef<'_, Schema>) -> T
where
T: Clone + 'static,
{
ctx.get(self)
}
pub fn clear(&self, ctx: &TypedContext<Schema>) {
ctx.inner.clear_slot(self.raw.id);
ctx.inner.flush_effects_after_invalidation();
}
}
impl<Schema, T> Clone for TypedSlotHandle<Schema, T> {
fn clone(&self) -> Self {
*self
}
}
impl<Schema, T> Copy for TypedSlotHandle<Schema, T> {}
impl<Schema, T> TypedCellHandle<Schema, T> {
fn new(raw: CellHandle<T>) -> Self {
Self {
raw,
_schema: PhantomData,
}
}
pub fn raw(&self) -> CellHandle<T> {
self.raw
}
pub fn get(&self, ctx: &TypedContext<Schema>) -> T
where
T: Clone + 'static,
{
ctx.get_cell(self)
}
pub fn get_ref(&self, ctx: &TypedContextRef<'_, Schema>) -> T
where
T: Clone + 'static,
{
ctx.get_cell(self)
}
pub fn set(&self, ctx: &TypedContext<Schema>, value: T)
where
T: PartialEq + 'static,
{
ctx.set_cell(self, value);
}
pub fn clear_dependents(&self, ctx: &TypedContext<Schema>) {
ctx.inner.clear_cell_dependents(self.raw.id);
}
}
impl<Schema, T> Clone for TypedCellHandle<Schema, T> {
fn clone(&self) -> Self {
*self
}
}
impl<Schema, T> Copy for TypedCellHandle<Schema, T> {}
#[cfg(feature = "thread-safe")]
impl<Schema> TypedThreadSafeContext<Schema> {
pub fn new() -> Self {
Self {
inner: ThreadSafeContext::new(),
_schema: PhantomData,
}
}
pub fn raw(&self) -> &ThreadSafeContext {
&self.inner
}
pub fn as_ref(&self) -> TypedThreadSafeContextRef<'_, Schema> {
TypedThreadSafeContextRef::new(&self.inner)
}
pub fn cell<T>(&self, value: T) -> TypedThreadSafeCellHandle<Schema, T>
where
T: PartialEq + Send + Sync + 'static,
{
TypedThreadSafeCellHandle::new(self.inner.cell(value))
}
pub fn slot<T, F>(&self, compute: F) -> TypedThreadSafeSlotHandle<Schema, T>
where
T: Send + Sync + 'static,
F: for<'a> Fn(&TypedThreadSafeContextRef<'a, Schema>) -> T + Send + Sync + 'static,
{
let raw = self.inner.slot(move |ctx| {
let typed = TypedThreadSafeContextRef::new(ctx);
compute(&typed)
});
TypedThreadSafeSlotHandle::new(raw)
}
pub fn computed<T, F>(&self, compute: F) -> TypedThreadSafeSlotHandle<Schema, T>
where
T: Send + Sync + 'static,
F: for<'a> Fn(&TypedThreadSafeContextRef<'a, Schema>) -> T + Send + Sync + 'static,
{
self.slot(compute)
}
pub fn memo<T, F>(&self, compute: F) -> TypedThreadSafeSlotHandle<Schema, T>
where
T: PartialEq + Send + Sync + 'static,
F: for<'a> Fn(&TypedThreadSafeContextRef<'a, Schema>) -> T + Send + Sync + 'static,
{
let raw = self.inner.memo(move |ctx| {
let typed = TypedThreadSafeContextRef::new(ctx);
compute(&typed)
});
TypedThreadSafeSlotHandle::new(raw)
}
pub fn get<T>(&self, handle: &TypedThreadSafeSlotHandle<Schema, T>) -> T
where
T: Clone + Send + Sync + 'static,
{
self.inner.get(&handle.raw)
}
pub fn get_cell<T>(&self, handle: &TypedThreadSafeCellHandle<Schema, T>) -> T
where
T: Clone + Send + Sync + 'static,
{
self.inner.get_cell(&handle.raw)
}
pub fn set_cell<T>(&self, handle: &TypedThreadSafeCellHandle<Schema, T>, value: T)
where
T: PartialEq + Send + Sync + 'static,
{
self.inner.set_cell(&handle.raw, value);
}
pub fn is_set<T>(&self, handle: &TypedThreadSafeSlotHandle<Schema, T>) -> bool
where
T: Send + Sync + 'static,
{
self.inner.is_set(&handle.raw)
}
}
#[cfg(feature = "thread-safe")]
impl<Schema> Clone for TypedThreadSafeContext<Schema> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
_schema: PhantomData,
}
}
}
#[cfg(feature = "thread-safe")]
impl<Schema> Default for TypedThreadSafeContext<Schema> {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "thread-safe")]
impl<'a, Schema> TypedThreadSafeContextRef<'a, Schema> {
fn new(inner: &'a ThreadSafeContext) -> Self {
Self {
inner,
_schema: PhantomData,
}
}
pub fn raw(&self) -> &'a ThreadSafeContext {
self.inner
}
pub fn get<T>(&self, handle: &TypedThreadSafeSlotHandle<Schema, T>) -> T
where
T: Clone + Send + Sync + 'static,
{
self.inner.get(&handle.raw)
}
pub fn get_cell<T>(&self, handle: &TypedThreadSafeCellHandle<Schema, T>) -> T
where
T: Clone + Send + Sync + 'static,
{
self.inner.get_cell(&handle.raw)
}
}
#[cfg(feature = "thread-safe")]
impl<Schema, T> TypedThreadSafeSlotHandle<Schema, T> {
fn new(raw: SlotHandle<T>) -> Self {
Self {
raw,
_schema: PhantomData,
}
}
pub fn raw(&self) -> SlotHandle<T> {
self.raw
}
pub fn get(&self, ctx: &TypedThreadSafeContext<Schema>) -> T
where
T: Clone + Send + Sync + 'static,
{
ctx.get(self)
}
pub fn get_ref(&self, ctx: &TypedThreadSafeContextRef<'_, Schema>) -> T
where
T: Clone + Send + Sync + 'static,
{
ctx.get(self)
}
pub fn clear(&self, ctx: &TypedThreadSafeContext<Schema>) {
ctx.inner.clear(&self.raw);
}
}
#[cfg(feature = "thread-safe")]
impl<Schema, T> Clone for TypedThreadSafeSlotHandle<Schema, T> {
fn clone(&self) -> Self {
*self
}
}
#[cfg(feature = "thread-safe")]
impl<Schema, T> Copy for TypedThreadSafeSlotHandle<Schema, T> {}
#[cfg(feature = "thread-safe")]
impl<Schema, T> TypedThreadSafeCellHandle<Schema, T> {
fn new(raw: CellHandle<T>) -> Self {
Self {
raw,
_schema: PhantomData,
}
}
pub fn raw(&self) -> CellHandle<T> {
self.raw
}
pub fn get(&self, ctx: &TypedThreadSafeContext<Schema>) -> T
where
T: Clone + Send + Sync + 'static,
{
ctx.get_cell(self)
}
pub fn get_ref(&self, ctx: &TypedThreadSafeContextRef<'_, Schema>) -> T
where
T: Clone + Send + Sync + 'static,
{
ctx.get_cell(self)
}
pub fn set(&self, ctx: &TypedThreadSafeContext<Schema>, value: T)
where
T: PartialEq + Send + Sync + 'static,
{
ctx.set_cell(self, value);
}
pub fn clear_dependents(&self, ctx: &TypedThreadSafeContext<Schema>) {
ctx.inner.clear_cell_dependents(&self.raw);
}
}
#[cfg(feature = "thread-safe")]
impl<Schema, T> Clone for TypedThreadSafeCellHandle<Schema, T> {
fn clone(&self) -> Self {
*self
}
}
#[cfg(feature = "thread-safe")]
impl<Schema, T> Copy for TypedThreadSafeCellHandle<Schema, T> {}
#[cfg(test)]
mod tests {
use super::*;
enum CycleSchema {}
#[test]
fn typed_context_binds_slot_and_cell_handles_to_schema() {
let ctx = TypedContext::<CycleSchema>::new();
let input = ctx.cell(2);
let doubled = ctx.slot(move |ctx| ctx.get_cell(&input) * 2);
assert_eq!(ctx.get(&doubled), 4);
ctx.set_cell(&input, 3);
assert_eq!(doubled.get(&ctx), 6);
}
#[cfg(feature = "thread-safe")]
#[test]
fn typed_thread_safe_context_binds_slot_and_cell_handles_to_schema() {
let ctx = TypedThreadSafeContext::<CycleSchema>::new();
let input = ctx.cell(2);
let doubled = ctx.slot(move |ctx| ctx.get_cell(&input) * 2);
assert_eq!(ctx.get(&doubled), 4);
ctx.set_cell(&input, 3);
assert_eq!(doubled.get(&ctx), 6);
}
}