leptos_windowing/loaders/
internal_loader.rs

1use std::{fmt::Debug, ops::Range};
2
3use super::{ExactLoader, LoadedItems, Loader, MemoryLoader, PaginatedCount, PaginatedLoader};
4
5/// This is the trait for the actually used internal loaders.
6/// This trait is automatically implemented for all the user facing loader traits.
7pub trait InternalLoader<M> {
8    /// If this Some(...) then the data will be loaded in chunks of this size.
9    /// This is useful for paginated data sources.
10    const CHUNK_SIZE: Option<usize> = None;
11
12    /// The type of items that will be loaded.
13    type Item;
14
15    /// The type of the query data that will be used to load items.
16    ///
17    /// Can be used to filter or sort the items for example.
18    type Query;
19
20    /// The type of errors that can occur during loading.
21    type Error: Debug + 'static;
22
23    /// Loads the items respecting the given `range` and `query` together with `CHUNK_SIZE`.
24    fn load_items(
25        &self,
26        range: Range<usize>,
27        query: &Self::Query,
28    ) -> impl Future<Output = Result<LoadedItems<Self::Item>, Self::Error>> {
29        let corrected_range = if let Some(chunk_size) = Self::CHUNK_SIZE {
30            let Range { start, end } = range;
31            let chunk_start = (start / chunk_size) * chunk_size;
32            let chunk_end = end.div_ceil(chunk_size) * chunk_size;
33            chunk_start..chunk_end
34        } else {
35            range
36        };
37
38        self.load_items_inner(corrected_range, query)
39    }
40
41    /// Don't call this directly. Call `load_items` instead.
42    ///
43    /// Loads the items respecting the given `range` and `query`.
44    /// This does not respect `CHUNK_SIZE`.
45    fn load_items_inner(
46        &self,
47        range: Range<usize>,
48        query: &Self::Query,
49    ) -> impl Future<Output = Result<LoadedItems<Self::Item>, Self::Error>>;
50
51    /// The total number of items of this data source.
52    ///
53    /// Returns `Ok(None)` if unknown (which is the default).
54    fn item_count(
55        &self,
56        _query: &Self::Query,
57    ) -> impl Future<Output = Result<Option<usize>, Self::Error>> {
58        async { Ok(None) }
59    }
60}
61
62pub struct LoaderMarker;
63
64impl<L> InternalLoader<LoaderMarker> for L
65where
66    L: Loader,
67{
68    const CHUNK_SIZE: Option<usize> = L::CHUNK_SIZE;
69
70    type Item = L::Item;
71    type Query = L::Query;
72    type Error = L::Error;
73
74    #[inline]
75    async fn load_items_inner(
76        &self,
77        range: Range<usize>,
78        query: &Self::Query,
79    ) -> Result<LoadedItems<Self::Item>, Self::Error> {
80        Loader::load_items(self, range, query).await
81    }
82
83    #[inline]
84    async fn item_count(&self, query: &Self::Query) -> Result<Option<usize>, Self::Error> {
85        Loader::item_count(self, query).await
86    }
87}
88
89pub struct ExactLoaderMarker;
90
91impl<L> InternalLoader<ExactLoaderMarker> for L
92where
93    L: ExactLoader,
94{
95    type Item = L::Item;
96    type Query = L::Query;
97    type Error = L::Error;
98
99    #[inline]
100    async fn load_items_inner(
101        &self,
102        range: Range<usize>,
103        query: &Self::Query,
104    ) -> Result<LoadedItems<Self::Item>, Self::Error> {
105        ExactLoader::load_items(self, range.clone(), query)
106            .await
107            .map(|items| LoadedItems { items, range })
108    }
109
110    #[inline]
111    async fn item_count(&self, query: &Self::Query) -> Result<Option<usize>, Self::Error> {
112        ExactLoader::item_count(self, query).await
113    }
114}
115
116pub struct MemoryLoaderMarker;
117
118impl<L> InternalLoader<MemoryLoaderMarker> for L
119where
120    L: MemoryLoader,
121{
122    type Item = L::Item;
123    type Query = L::Query;
124    type Error = ();
125
126    #[inline]
127    async fn load_items_inner(
128        &self,
129        range: Range<usize>,
130        query: &Self::Query,
131    ) -> Result<LoadedItems<Self::Item>, Self::Error> {
132        Ok(LoadedItems {
133            items: self.load_items(range.clone(), query),
134            range,
135        })
136    }
137
138    #[inline]
139    async fn item_count(&self, query: &Self::Query) -> Result<Option<usize>, Self::Error> {
140        Ok(Some(MemoryLoader::item_count(self, query)))
141    }
142}
143
144pub struct PaginatedLoaderMarker;
145
146impl<L> InternalLoader<PaginatedLoaderMarker> for L
147where
148    L: PaginatedLoader,
149{
150    const CHUNK_SIZE: Option<usize> = Some(L::PAGE_ITEM_COUNT);
151
152    type Item = L::Item;
153    type Query = L::Query;
154    type Error = L::Error;
155
156    #[inline]
157    async fn load_items_inner(
158        &self,
159        range: Range<usize>,
160        query: &Self::Query,
161    ) -> Result<LoadedItems<Self::Item>, Self::Error> {
162        let Range { start, end } = range;
163
164        debug_assert_eq!(start % L::PAGE_ITEM_COUNT, 0);
165        debug_assert_eq!((end - start) % L::PAGE_ITEM_COUNT, 0);
166
167        let mut loaded = Vec::with_capacity(end - start);
168
169        for cur_start in (start..end).step_by(L::PAGE_ITEM_COUNT) {
170            loaded.extend(
171                self.load_page(cur_start / L::PAGE_ITEM_COUNT, query)
172                    .await?,
173            );
174        }
175
176        let len = loaded.len();
177        Ok(LoadedItems {
178            items: loaded,
179            range: start..start + len,
180        })
181    }
182
183    #[inline]
184    async fn item_count(&self, query: &Self::Query) -> Result<Option<usize>, Self::Error> {
185        PaginatedLoader::count(self, query).await.map(|count| {
186            count.map(|count| match count {
187                PaginatedCount::Items(item_count) => item_count,
188                PaginatedCount::Pages(page_count) => page_count * L::PAGE_ITEM_COUNT,
189            })
190        })
191    }
192}