ribir_core/state/
splitted_state.rs

1use std::cell::Cell;
2
3use ribir_algo::Sc;
4use rxrust::{
5  ops::box_it::CloneableBoxOp,
6  prelude::{BoxIt, ObservableExt},
7};
8
9use super::{
10  state_cell::PartData, MapWriterAsReader, ModifyScope, Notifier, ReadRef, StateReader,
11  StateWatcher, StateWriter, WriteRef, WriterControl,
12};
13use crate::{
14  context::BuildCtx,
15  prelude::AppCtx,
16  state::state_cell::ValueMutRef,
17  ticker::Instant,
18  widget::{Render, RenderBuilder, Widget},
19};
20
21/// A writer splitted writer from another writer, and has its own notifier.
22pub struct SplittedWriter<O, W> {
23  origin: O,
24  splitter: W,
25  notifier: Notifier,
26  batched_modify: Sc<Cell<ModifyScope>>,
27  create_at: Instant,
28  last_modified: Sc<Cell<Instant>>,
29  ref_count: Sc<Cell<usize>>,
30}
31
32impl<O, W> Drop for SplittedWriter<O, W> {
33  fn drop(&mut self) {
34    if self.ref_count.get() == 1 {
35      let mut notifier = self.notifier.clone();
36      // we use an async task to unsubscribe to wait the batched modifies to be
37      // notified.
38      let _ = AppCtx::spawn_local(async move {
39        notifier.unsubscribe();
40      });
41    }
42  }
43}
44
45impl<V, O, W> StateReader for SplittedWriter<O, W>
46where
47  Self: 'static,
48  O: StateWriter,
49  W: Fn(&mut O::Value) -> PartData<V> + Clone,
50{
51  type Value = V;
52  type OriginReader = O;
53  type Reader = MapWriterAsReader<O::Reader, W>;
54
55  #[track_caller]
56  fn read(&self) -> ReadRef<Self::Value> {
57    ReadRef::mut_as_ref_map(self.origin.read(), &self.splitter)
58  }
59
60  #[inline]
61  fn clone_reader(&self) -> Self::Reader {
62    MapWriterAsReader { origin: self.origin.clone_reader(), part_map: self.splitter.clone() }
63  }
64
65  #[inline]
66  fn origin_reader(&self) -> &Self::OriginReader { &self.origin }
67
68  #[inline]
69  fn try_into_value(self) -> Result<Self::Value, Self>
70  where
71    Self::Value: Sized,
72  {
73    Err(self)
74  }
75}
76
77// fixme: we will remove the stamp check after #521 is fixed.
78struct StampCheck<R> {
79  stamp: Instant,
80  reader: R,
81}
82
83impl<R: StateReader> CloneableChecker for StampCheck<R> {
84  fn check(&self) -> bool { true }
85  fn box_clone(&self) -> Box<dyn CloneableChecker> {
86    Box::new(StampCheck { stamp: self.stamp, reader: self.reader.clone_reader() })
87  }
88}
89
90trait CloneableChecker {
91  fn check(&self) -> bool;
92  fn box_clone(&self) -> Box<dyn CloneableChecker>;
93}
94
95impl Clone for Box<dyn CloneableChecker> {
96  fn clone(&self) -> Self { self.box_clone() }
97}
98
99impl<V, O, W> StateWatcher for SplittedWriter<O, W>
100where
101  Self: 'static,
102  O: StateWriter,
103  W: Fn(&mut O::Value) -> PartData<V> + Clone,
104{
105  fn raw_modifies(&self) -> CloneableBoxOp<'static, ModifyScope, std::convert::Infallible> {
106    let origin = self.origin.clone_reader();
107    let create_at = self.create_at;
108
109    // create a cloneable checker to check.
110    let checker: Box<dyn CloneableChecker> =
111      Box::new(StampCheck { stamp: create_at, reader: origin.clone_reader() });
112
113    self
114      .notifier
115      .raw_modifies()
116      .take_while(move |_| checker.check())
117      .box_it()
118  }
119}
120
121impl<V, O, W> StateWriter for SplittedWriter<O, W>
122where
123  Self: 'static,
124  O: StateWriter,
125  W: Fn(&mut O::Value) -> PartData<V> + Clone,
126{
127  type Writer = SplittedWriter<O::Writer, W>;
128  type OriginWriter = O;
129
130  #[inline]
131  fn write(&self) -> WriteRef<Self::Value> { self.split_ref(self.origin.write()) }
132
133  #[inline]
134  fn silent(&self) -> WriteRef<Self::Value> { self.split_ref(self.origin.silent()) }
135
136  #[inline]
137  fn shallow(&self) -> WriteRef<Self::Value> { self.split_ref(self.origin.shallow()) }
138
139  fn clone_writer(&self) -> Self::Writer {
140    SplittedWriter {
141      origin: self.origin.clone_writer(),
142      splitter: self.splitter.clone(),
143      notifier: self.notifier.clone(),
144      batched_modify: self.batched_modify.clone(),
145      last_modified: self.last_modified.clone(),
146      create_at: self.create_at,
147      ref_count: self.ref_count.clone(),
148    }
149  }
150
151  #[inline]
152  fn origin_writer(&self) -> &Self::OriginWriter { &self.origin }
153}
154
155impl<V, O, W> WriterControl for SplittedWriter<O, W>
156where
157  Self: 'static,
158  O: StateWriter,
159  W: Fn(&mut O::Value) -> PartData<V> + Clone,
160{
161  #[inline]
162  fn batched_modifies(&self) -> &Cell<ModifyScope> { &self.batched_modify }
163
164  #[inline]
165  fn notifier(&self) -> &Notifier { &self.notifier }
166
167  #[inline]
168  fn dyn_clone(&self) -> Box<dyn WriterControl> { Box::new(self.clone_writer()) }
169}
170
171impl<V, O, W> SplittedWriter<O, W>
172where
173  Self: 'static,
174  O: StateWriter,
175  W: Fn(&mut O::Value) -> PartData<V> + Clone,
176{
177  pub(super) fn new(origin: O, mut_map: W) -> Self {
178    let create_at = Instant::now();
179
180    Self {
181      origin,
182      splitter: mut_map,
183      notifier: Notifier::default(),
184      batched_modify: <_>::default(),
185      last_modified: Sc::new(Cell::new(create_at)),
186      create_at,
187      ref_count: Sc::new(Cell::new(1)),
188    }
189  }
190
191  #[track_caller]
192  fn split_ref<'a>(&'a self, mut orig: WriteRef<'a, O::Value>) -> WriteRef<'a, V> {
193    let modify_scope = orig.modify_scope;
194
195    // the origin mark as a silent write, because split writer not effect the origin
196    // state in ribir framework level. But keep notify in the data level.
197    assert!(!orig.modified);
198    orig.modify_scope.remove(ModifyScope::FRAMEWORK);
199    orig.modified = true;
200    let value =
201      ValueMutRef { value: (self.splitter)(&mut orig.value), borrow: orig.value.borrow.clone() };
202
203    WriteRef { value, modified: false, modify_scope, control: self }
204  }
205}
206
207impl<V, O, W> RenderBuilder for SplittedWriter<O, W>
208where
209  O: StateWriter,
210  W: Fn(&mut O::Value) -> PartData<V> + Clone + 'static,
211  V: Render,
212{
213  fn build(self, ctx: &BuildCtx) -> Widget {
214    MapWriterAsReader { origin: self.origin.clone_reader(), part_map: self.splitter.clone() }
215      .build(ctx)
216  }
217}