1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//! A value a copyable handle reaches in the runtime, without snapshots.
use std::{fmt, hash::Hash, marker::PhantomData, rc::Rc};
use crate::runtime::{self, RuntimeHandle, RuntimeId, StateId};
/// A value kept in a runtime, reached through a handle that copies like a
/// [`MutableState`](crate::MutableState).
///
/// A state holder that is `Copy` keeps its shared, interior-mutable parts
/// here: a read finds the value in the runtime's cells, with no snapshot
/// records to walk and no scope subscribed. Nothing recomposes when the value
/// changes, so what a composition reads goes in a `MutableState` instead.
///
/// The value is owned as a state is: made while a
/// [`remember`](crate::remember) builds its value, it belongs to that slot
/// and goes with it; made anywhere else, it lives as long as the runtime.
///
/// ```
/// use std::cell::Cell;
///
/// use cranpose_core::{DefaultScheduler, Runtime, RuntimeCell, scheduler_ref};
///
/// let runtime = Runtime::new(scheduler_ref(DefaultScheduler));
/// let count = RuntimeCell::with_runtime(Cell::new(0), runtime.handle());
/// let copy = count;
/// copy.with(|count| count.set(count.get() + 1));
/// assert_eq!(count.with(Cell::get), 1);
/// ```
pub struct RuntimeCell<T: 'static> {
id: StateId,
runtime_id: RuntimeId,
_marker: PhantomData<fn() -> T>,
}
impl<T: 'static> RuntimeCell<T> {
/// Keeps `value` in the current runtime. Panics without one.
pub fn new(value: T) -> Self {
Self::with_runtime(value, crate::hooks::current_runtime("RuntimeCell::new"))
}
/// Keeps `value` in `runtime`.
pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
Self {
id: runtime.alloc_owned_plain(value),
runtime_id: runtime.id(),
_marker: PhantomData,
}
}
/// Whether the value still lives: its owner and its runtime keep it.
pub fn is_alive(&self) -> bool {
self.try_with(|_| ()).is_some()
}
/// A handle to the value, or `None` once it is gone. The handle keeps the
/// value alive.
pub fn try_get(&self) -> Option<Rc<T>> {
runtime::with_state_arena_by_id(self.runtime_id, |arena| arena.plain::<T>(self.id))?
}
/// A handle to the value. Panics once it is gone.
pub fn get(&self) -> Rc<T> {
self.try_get()
.unwrap_or_else(|| panic!("runtime cell {:?} is no longer alive", self.id))
}
/// Runs `f` on the value, or returns `None` once it is gone.
pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
self.try_get().map(|value| f(&value))
}
/// Runs `f` on the value. Panics once it is gone.
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
f(&self.get())
}
}
impl<T: 'static> Clone for RuntimeCell<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: 'static> Copy for RuntimeCell<T> {}
impl<T: 'static> PartialEq for RuntimeCell<T> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id && self.runtime_id == other.runtime_id
}
}
impl<T: 'static> Eq for RuntimeCell<T> {}
impl<T: 'static> Hash for RuntimeCell<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
self.runtime_id.hash(state);
}
}
impl<T: 'static> fmt::Debug for RuntimeCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RuntimeCell")
.field("id", &self.id)
.field("runtime_id", &self.runtime_id)
.finish()
}
}