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
110
111
112
113
114
115
116
117
118
119
120
use crate::reactive::cell::CellCommit;
use crate::reactive::cell::ReactiveCell;
use crate::reactive::error::WriteValue;
use crate::store::StoreBackend;
use crate::store::facts::Facts;
use crate::{ReactiveMap, ReactiveMapKey, ReactiveMapValue};
use amethystate_core::primitives::map_core::MapEntryPath;
use amethystate_core::{MapChange, Signal};
use error_stack::Report;
use std::sync::Arc;
impl<K, V> ReactiveMap<K, V>
where
K: ReactiveMapKey,
V: ReactiveMapValue,
{
/// An entry cell that keeps the map alive, for handing one entry somewhere
/// and letting go of the map.
///
/// The cell owns the map, so it stays readable and writable for as long as
/// it is held. [`ReactiveMap::entry_cell`] is the view onto a map something
/// else keeps.
///
/// The map it takes is the one it keeps. Nothing is taken from a struct by
/// doing this: a generated accessor hands out an `Arc::clone` of the map,
/// so the cell becomes one more owner of it.
pub fn into_entry_cell(self, key: K) -> ReactiveCell<V> {
let cell = self.entry_cell(key);
cell.owning(Arc::new(self))
}
/// A live cell over one map entry.
///
/// The cell is a view and keeps only a weak reference to the map, so once
/// the map is dropped every write fails and
/// [`get`](ReactiveCell::get) reads `None`. Where the cell is the only
/// handle that survives, use [`ReactiveMap::into_entry_cell`].
///
/// An absent key reads `None`, and writing to one fails - creating the
/// entry is [`ReactiveMap::insert`]'s job.
///
/// ```
/// # use amethystate::StoreBuilder;
/// # let path = amethystate_core::test_utils::TempPath::new("doc");
/// # let store = StoreBuilder::new(&*path).build().unwrap();
/// let widths = store.kv().map::<String, u64>("columns").unwrap();
/// widths.insert("cpu".to_string(), &100).unwrap();
/// let cpu = widths.entry_cell("cpu".to_string());
///
/// assert_eq!(cpu.get(), Some(100));
///
/// // Writing through the cell writes the map entry.
/// cpu.set(120).unwrap();
/// assert_eq!(widths.get("cpu"), Some(120));
///
/// // And a write to the map reaches the cell.
/// widths.update("cpu", &200).unwrap();
/// assert_eq!(cpu.get(), Some(200));
///
/// // Removing the key empties the cell, and it refuses to put it back.
/// widths.remove("cpu").unwrap();
/// assert_eq!(cpu.get(), None);
/// assert!(cpu.set(80).is_err());
/// ```
pub fn entry_cell(&self, key: K) -> ReactiveCell<V> {
let cache = Signal::new(self.get(&key));
let sink = cache.clone();
let read = self.subscribe_key(key.clone(), move |change| {
let (next, source) = match change {
MapChange::Insert { value, source, .. }
| MapChange::Update {
new_value: value,
source,
..
} => (Some(value.clone()), *source),
MapChange::Remove { source, .. } | MapChange::Clear { source } => (None, *source),
};
sink.set_forwarded(next, source);
});
let origin = self.inner.instance_id;
let weak = Arc::downgrade(&self.inner);
let write_key = key;
let flush = weak.clone();
let start = weak.clone();
let commit = CellCommit {
now: Arc::new(move || {
let inner = flush.upgrade().ok_or(WriteValue::SourceGone)?;
inner
.store
.flush_prefix(&inner.path)
.map_err(Report::from)
.attach_key(&inner.path)
.map_err(|why| WriteValue::from_store(&inner.path, why))
}),
start: Arc::new(move || match start.upgrade() {
Some(inner) => inner.store.flush_async(),
None => crate::store::Commit::gone(),
}),
};
let alive = weak.clone();
let absent = self.inner.path.entry(write_key.as_ref());
ReactiveCell::from_parts(
cache,
Arc::new(move |value: V| {
let inner = weak.upgrade().ok_or(WriteValue::SourceGone)?;
let map = ReactiveMap::<K, V> { inner };
map.update(&write_key, &value)
}),
Some(Arc::new(move || alive.strong_count() > 0)),
Some(absent),
origin,
Some(commit),
Some(Arc::new(read)),
)
}
}