1use std::cell::RefCell;
2use std::collections::hash_map::Entry;
3use std::collections::HashMap;
4use std::rc::Rc;
5
6use yew::platform::pinned::oneshot;
7use yew::prelude::*;
8
9use super::traits::{Mutation, MutationResult};
10use crate::future_notion;
11use crate::root_state::BounceStates;
12use crate::states::future_notion::Deferred;
13use crate::states::input_selector::InputSelector;
14use crate::states::notion::WithNotion;
15use crate::states::slice::Slice;
16use crate::utils::Id;
17
18#[derive(Default, PartialEq, Debug, Clone, Eq, Hash, PartialOrd, Ord, Copy)]
20pub(super) struct HandleId(Id);
21
22#[derive(Default, PartialEq, Debug, Clone, Eq, Hash, PartialOrd, Ord, Copy)]
23pub(super) struct MutationId(Id);
24
25#[derive(PartialEq, Debug)]
26pub(super) enum MutationSliceValue<T>
27where
28 T: Mutation + 'static,
29{
30 Idle,
31 Loading {
32 id: MutationId,
33 },
34 Completed {
35 id: MutationId,
36 result: MutationResult<T>,
37 },
38 Outdated {
39 id: MutationId,
40 result: MutationResult<T>,
41 },
42}
43
44impl<T> Clone for MutationSliceValue<T>
45where
46 T: Mutation + 'static,
47{
48 fn clone(&self) -> Self {
49 match self {
50 Self::Idle => Self::Idle,
51 Self::Loading { id } => Self::Loading { id: *id },
52 Self::Completed { id, result } => Self::Completed {
53 id: *id,
54 result: result.clone(),
55 },
56 Self::Outdated { id, result } => Self::Outdated {
57 id: *id,
58 result: result.clone(),
59 },
60 }
61 }
62}
63
64pub(super) struct RunMutationInput<T>
65where
66 T: Mutation,
67{
68 pub handle_id: HandleId,
69 pub mutation_id: MutationId,
70 pub input: Rc<T::Input>,
71 pub sender: RefCell<Option<oneshot::Sender<MutationResult<T>>>>,
72}
73
74#[future_notion(RunMutation)]
75pub(super) async fn run_mutation<T>(
76 states: &BounceStates,
77 input: &RunMutationInput<T>,
78) -> MutationResult<T>
79where
80 T: Mutation + 'static,
81{
82 let result = T::run(states, input.input.clone()).await;
83
84 if let Some(m) = input.sender.borrow_mut().take() {
85 let _result = m.send(result.clone());
86 }
87
88 result
89}
90
91pub(super) enum MutationSliceAction {
92 Create(HandleId),
94 Destroy(HandleId),
96}
97
98#[derive(Slice, Debug)]
99#[bounce(with_notion(Deferred<RunMutation<T>>))]
100pub(super) struct MutationSlice<T>
101where
102 T: Mutation + 'static,
103{
104 ctr: u64,
105 mutations: HashMap<HandleId, MutationSliceValue<T>>,
106}
107
108impl<T> PartialEq for MutationSlice<T>
109where
110 T: Mutation + 'static,
111{
112 fn eq(&self, rhs: &Self) -> bool {
113 self.ctr == rhs.ctr
114 }
115}
116
117impl<T> Default for MutationSlice<T>
118where
119 T: Mutation + 'static,
120{
121 fn default() -> Self {
122 Self {
123 ctr: 0,
124 mutations: HashMap::new(),
125 }
126 }
127}
128
129impl<T> Clone for MutationSlice<T>
130where
131 T: Mutation + 'static,
132{
133 fn clone(&self) -> Self {
134 Self {
135 ctr: self.ctr,
136 mutations: self.mutations.clone(),
137 }
138 }
139}
140
141impl<T> Reducible for MutationSlice<T>
142where
143 T: Mutation + 'static,
144{
145 type Action = MutationSliceAction;
146
147 fn reduce(mut self: Rc<Self>, action: Self::Action) -> Rc<Self> {
148 {
149 let this = Rc::make_mut(&mut self);
150 match action {
153 Self::Action::Create(id) => {
154 this.mutations.insert(id, MutationSliceValue::Idle);
155 }
156
157 Self::Action::Destroy(id) => {
158 this.mutations.remove(&id);
159 }
160 }
161 }
162
163 self
164 }
165}
166
167impl<T> WithNotion<Deferred<RunMutation<T>>> for MutationSlice<T>
168where
169 T: Mutation + 'static,
170{
171 fn apply(mut self: Rc<Self>, notion: Rc<Deferred<RunMutation<T>>>) -> Rc<Self> {
172 match notion.as_ref() {
173 Deferred::Completed {
174 ref input,
175 ref output,
176 } => {
177 let this = Rc::make_mut(&mut self);
178 this.ctr += 1;
179
180 match this.mutations.entry(input.handle_id) {
181 Entry::Vacant(_m) => {
182 return self; }
184 Entry::Occupied(mut m) => {
185 let m = m.get_mut();
186 match m {
187 MutationSliceValue::Loading { id }
188 | MutationSliceValue::Completed { id, .. }
189 | MutationSliceValue::Outdated { id, .. } => {
190 if *id <= input.mutation_id {
192 *m = MutationSliceValue::Completed {
193 id: input.mutation_id,
194 result: output.as_ref().clone(),
195 };
196 }
197 }
198 MutationSliceValue::Idle => {
199 *m = MutationSliceValue::Completed {
200 id: input.mutation_id,
201 result: output.as_ref().clone(),
202 };
203 }
204 }
205 }
206 }
207 }
208 Deferred::Pending { input } => {
209 let this = Rc::make_mut(&mut self);
210 this.ctr += 1;
211
212 match this.mutations.entry(input.handle_id) {
213 Entry::Vacant(_m) => {
214 return self; }
216 Entry::Occupied(mut m) => {
217 let m = m.get_mut();
218 match m {
219 MutationSliceValue::Loading { .. } => {}
220 MutationSliceValue::Completed { id, result } => {
221 *m = MutationSliceValue::Outdated {
222 id: *id,
223 result: result.clone(),
224 };
225 }
226 MutationSliceValue::Outdated { .. } => {}
227 MutationSliceValue::Idle => {
228 *m = MutationSliceValue::Loading {
229 id: input.mutation_id,
230 };
231 }
232 }
233 }
234 }
235 }
236 Deferred::Outdated { .. } => {}
237 }
238
239 self
240 }
241}
242
243#[derive(PartialEq)]
244pub(super) struct MutationSelector<T>
245where
246 T: Mutation + 'static,
247{
248 pub id: Option<MutationId>,
249 pub value: Option<MutationSliceValue<T>>,
250}
251
252impl<T> InputSelector for MutationSelector<T>
253where
254 T: Mutation + 'static,
255{
256 type Input = HandleId;
257 fn select(states: &BounceStates, input: Rc<HandleId>) -> Rc<Self> {
258 let value = states
259 .get_slice_value::<MutationSlice<T>>()
260 .mutations
261 .get(&input)
262 .cloned();
263
264 let id = value.as_ref().and_then(|m| match m {
265 MutationSliceValue::Loading { id }
266 | MutationSliceValue::Completed { id, .. }
267 | MutationSliceValue::Outdated { id, .. } => Some(*id),
268 MutationSliceValue::Idle => None,
269 });
270
271 Self { id, value }.into()
272 }
273}