leptos_windowing/
cache.rs1use leptos::prelude::*;
2use reactive_stores::{Store, StoreFieldIterator};
3use std::{
4 ops::{Index, Range},
5 sync::Arc,
6};
7
8use crate::{LoadedItems, item_state::ItemState};
9
10#[derive(Store, Debug)]
13pub struct Cache<T>
14where
15 T: Send + Sync + 'static,
16{
17 items: Vec<ItemState<T>>,
18 item_count: Option<usize>,
19}
20
21impl<T: Send + Sync + 'static> Default for Cache<T> {
22 fn default() -> Self {
23 Self {
24 items: Vec::new(),
25 item_count: None,
26 }
27 }
28}
29
30impl<T: Send + Sync + 'static> Cache<T> {
31 pub fn new_store() -> Store<Self> {
33 Store::new(Self::default())
34 }
35
36 #[inline]
37 pub fn len(&self) -> usize {
39 self.items.len()
40 }
41
42 pub fn is_empty(&self) -> bool {
43 self.items.is_empty()
44 }
45
46 #[inline]
47 pub fn resize(&mut self, len: usize) {
49 self.items.resize(len, ItemState::Placeholder);
50 }
51
52 pub fn grow(&mut self, len: usize) {
54 if self.items.len() < len {
55 self.items.resize(len, ItemState::Placeholder);
56 }
57 }
58
59 pub fn write_loading(this_store: Store<Self>, range: Range<usize>) {
61 if range.end > this_store.items().read().len() {
62 this_store
63 .items()
64 .write()
65 .resize(range.end, ItemState::Placeholder);
66 }
67
68 for row in &mut this_store
69 .items()
70 .iter_unkeyed()
71 .skip(range.start)
72 .take(range.len())
73 {
74 if let Some(mut row) = row.try_write() {
75 *row = ItemState::Loading;
76 }
77 }
78 }
79
80 pub fn write_loaded(
84 this_store: Store<Self>,
85 loading_result: Result<LoadedItems<T>, String>,
86 requested_load_range: Range<usize>,
87 ) {
88 match loading_result {
89 Ok(LoadedItems { items, range }) => {
90 #[cfg(debug_assertions)]
91 let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
92
93 if range.end > this_store.items().read_untracked().len()
94 && let Some(mut writer) = this_store.items().try_write()
95 {
96 writer.resize(range.end, ItemState::Placeholder);
97 }
98
99 for (self_row, loaded_row) in this_store
100 .items()
101 .iter_unkeyed()
102 .skip(range.start)
103 .zip(items)
104 {
105 if let Some(mut writer) = self_row.try_write() {
106 *writer = ItemState::Loaded(Arc::new(loaded_row));
107 }
108 }
109 }
110 Err(error) => {
111 let range = requested_load_range.start
112 ..requested_load_range
113 .end
114 .min(this_store.items().read().len());
115 if range.start >= range.end {
116 return;
117 }
118
119 for row in this_store.items().iter_unkeyed() {
120 if let Some(mut writer) = row.try_write() {
121 *writer = ItemState::Error(error.clone());
122 }
123 }
124 }
125 }
126 }
127
128 #[inline]
129 pub fn missing_range(&self, range_to_load: Range<usize>) -> Option<Range<usize>> {
134 let do_load_predicate = |item: &ItemState<T>| matches!(item, &ItemState::Placeholder);
135
136 if range_to_load.end <= range_to_load.start {
137 return None;
138 }
139
140 if range_to_load.start >= self.items.len() {
141 return Some(range_to_load);
142 }
143
144 let existing_range_end = self.items.len().min(range_to_load.end);
145
146 let slice = &self.items[range_to_load.start..existing_range_end];
147
148 let start = slice
149 .iter()
150 .position(do_load_predicate)
151 .unwrap_or(slice.len());
152 let start = start + range_to_load.start;
153
154 let mut end = if range_to_load.end >= self.items.len() {
155 range_to_load.end
156 } else {
157 slice.iter().rposition(do_load_predicate)? + range_to_load.start + 1
158 };
159
160 if let Some(item_count) = self.item_count {
161 end = end.min(item_count);
162 }
163
164 if end <= start {
165 return None;
166 }
167
168 Some(
169 start
170 ..end
171 .max(range_to_load.end)
172 .min(self.item_count.unwrap_or(usize::MAX)),
173 )
174 }
175
176 #[inline]
177 pub fn clear(this_store: Store<Self>) {
179 this_store.items().write().fill(ItemState::Placeholder);
180 this_store.item_count().set(None);
181 }
182}
183
184impl<T: Sync + Send> Index<Range<usize>> for Cache<T> {
185 type Output = [ItemState<T>];
186
187 #[inline]
188 fn index(&self, index: Range<usize>) -> &Self::Output {
189 &self.items[index]
190 }
191}
192
193impl<T: Send + Sync> Index<usize> for Cache<T> {
194 type Output = ItemState<T>;
195
196 #[inline]
197 fn index(&self, index: usize) -> &Self::Output {
198 &self.items[index]
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn test_missing_range() {
208 let cache = Cache::<i32>::new_store();
209
210 assert_eq!(cache.read_untracked().missing_range(0..10), Some(0..10));
211 assert_eq!(cache.read_untracked().missing_range(5..10), Some(5..10));
212
213 Cache::write_loaded(
214 cache,
215 Ok(LoadedItems {
216 items: (0..5).collect::<Vec<_>>(),
217 range: 0..5,
218 }),
219 0..5,
220 );
221
222 assert_eq!(cache.read_untracked().missing_range(0..10), Some(5..10));
223 assert_eq!(cache.read_untracked().missing_range(5..10), Some(5..10));
224 assert_eq!(cache.read_untracked().missing_range(5..20), Some(5..20));
225
226 Cache::write_loading(cache, 5..9);
227
228 assert_eq!(cache.read_untracked().missing_range(0..10), Some(9..10));
229 assert_eq!(cache.read_untracked().missing_range(5..10), Some(9..10));
230 assert_eq!(cache.read_untracked().missing_range(5..20), Some(9..20));
231 }
232}