leptos_struct_table/data_provider.rs
1#![allow(async_fn_in_trait)]
2
3use crate::ColumnSort;
4use std::collections::VecDeque;
5use std::fmt::Debug;
6use std::ops::Range;
7
8/// The trait that provides data for the `<TableContent>` component.
9/// Anything that is passed to the `rows` prop must implement this trait.
10///
11/// If you add `#[table(impl_vec_data_provider)]` to your row struct,
12/// this is automatically implemented for `Vec<Row>`.
13/// This way a simple list of items can be passed to the table.
14///
15/// This is also automatically implemented for any struct that implements
16/// [`PaginatedTableDataProvider`] or [`ExactTableDataProvider`].
17/// The first is a more convenient way of connecting to a paginated data source and the second is
18/// more convenient if you know you're always going to return exactly the requested range (except maybe
19/// at the end of the data).
20pub trait TableDataProvider<Row, Err: Debug = String> {
21 /// If Some(...), data will be loaded in chunks of this size. This is useful for paginated data sources.
22 /// If you have such a paginated data source, you probably want to implement `PaginatedTableDataProvider`
23 /// instead of this trait.
24 const CHUNK_SIZE: Option<usize> = None;
25
26 /// Get all data rows for the table specified by the range. This method is called when the table is rendered.
27 /// The range is determined by the visible rows and used to virtualize the table.
28 /// The parameter `range` is only determined by visibility and may be out of bounds. It is the
29 /// responsibility of the implementation to handle this case. Use [get_vec_range_clamped] to get a
30 /// range that is clamped to the length of the vector.
31 ///
32 /// It returns a `Vec` of all rows loaded and the range that these rows cover. Depending on
33 /// the data source you might not be able to load exactly the requested range; that's why
34 /// the actual loaded range is returned in addition to the rows. You should always return
35 /// at least the range that is requested or more. If you return less rows than requested,
36 /// it is assumed that the data source is done and there are no more rows to load.
37 ///
38 /// In the case of an error the returned error `String` is going to be displayed in a
39 /// in place of the failed rows.
40 async fn get_rows(&self, range: Range<usize>) -> Result<(Vec<Row>, Range<usize>), Err>;
41
42 /// The total number of rows in the table. Returns `None` if unknown (which is the default).
43 async fn row_count(&self) -> Option<usize> {
44 None
45 }
46
47 /// Set the sorting of the table. The sorting is a list of column names and the sort order sorted by priority.
48 /// The first entry in the list is the most important one.
49 /// The default implementation does nothing.
50 /// For example: `[(0, ColumnSort::Ascending), (1, ColumnSort::Descending)]`
51 /// will sort by name first and then by age.
52 /// Please note that after calling this method, data will be reloaded through [`get_rows`](TableDataProvider::get_rows).
53 #[allow(unused_variables)]
54 fn set_sorting(&mut self, sorting: &VecDeque<(usize, ColumnSort)>) {
55 // by default do nothing
56 }
57
58 /// Call `.track()` in this method on all signals that loading data relies on.
59 /// For example a search of filters. Please check the [paginated_rest_datasource example](https://github.com/Synphonyte/leptos-struct-table/blob/master/examples/paginated_rest_datasource/src/data_provider.rs)
60 fn track(&self) {
61 // by default do nothing
62 }
63}
64
65/// A paginated data source. This is meant to provide a more convenient way
66/// of connecting to a paginated data source instead of implementing [`TableDataProvider`] directly.
67///
68/// If you implement this for your struct, [`TableDataProvider`] is automatically implemented for you.
69///
70/// > Please note that this is independent from using [`DisplayStrategy::Pagination`] with [`TableContent`].
71/// > You do not have implement this trait if you're using pagination and you vice versa if you're not using pagination
72/// > you can still implement this trait. And in case if you use this trait together with pagination the
73/// > display row count can be different from the `PAGE_ROW_COUNT`.
74pub trait PaginatedTableDataProvider<Row, Err: Debug = String> {
75 /// How many rows per page
76 const PAGE_ROW_COUNT: usize;
77
78 /// Get all data rows for the table specified by the page index (starts a 0).
79 ///
80 /// If you return less than `PAGE_ROW_COUNT` rows, it is assumed that the end of the
81 /// data has been reached.
82 async fn get_page(&self, page_index: usize) -> Result<Vec<Row>, Err>;
83
84 /// The total number of rows in the table. Returns `None` if unknown (which is the default).
85 ///
86 /// By default this is computed from the [`page_count`] method. But if your data source
87 /// tells you the number of rows instead of the number of pages you should override this method.
88 async fn row_count(&self) -> Option<usize> {
89 self.page_count().await.map(|pc| pc * Self::PAGE_ROW_COUNT)
90 }
91
92 /// The total number of pages in the data source. Returns `None` if unknown (which is the default).
93 ///
94 /// If your data source gives you the number of rows instead of the number of pages
95 /// you should implement [`row_count`] instead of this method.
96 async fn page_count(&self) -> Option<usize> {
97 None
98 }
99
100 /// Same as [`TableDataProvider::set_sorting`]
101 #[allow(unused_variables)]
102 fn set_sorting(&mut self, sorting: &VecDeque<(usize, ColumnSort)>) {
103 // by default do nothing
104 }
105
106 /// Same as [`TableDataProvider::track`]
107 fn track(&self) {
108 // by default do nothing
109 }
110}
111
112impl<Row, Err, D> TableDataProvider<Row, Err> for D
113where
114 D: PaginatedTableDataProvider<Row, Err>,
115 Err: Debug,
116{
117 const CHUNK_SIZE: Option<usize> = Some(D::PAGE_ROW_COUNT);
118
119 async fn get_rows(&self, range: Range<usize>) -> Result<(Vec<Row>, Range<usize>), Err> {
120 let Range { start, end } = range;
121
122 debug_assert_eq!(start % D::PAGE_ROW_COUNT, 0);
123 debug_assert_eq!(end - start, D::PAGE_ROW_COUNT);
124
125 self.get_page(start / D::PAGE_ROW_COUNT).await.map(|rows| {
126 let len = rows.len();
127 (rows, start..start + len)
128 })
129 }
130
131 async fn row_count(&self) -> Option<usize> {
132 PaginatedTableDataProvider::<Row, Err>::row_count(self).await
133 }
134
135 fn set_sorting(&mut self, sorting: &VecDeque<(usize, ColumnSort)>) {
136 PaginatedTableDataProvider::<Row, Err>::set_sorting(self, sorting)
137 }
138
139 fn track(&self) {
140 PaginatedTableDataProvider::<Row, Err>::track(self)
141 }
142}
143
144/// Return `vec[range.start..range.end]` where `range` is clamped to the length of `vec`.
145pub fn get_vec_range_clamped<T: Clone>(
146 vec: &Vec<T>,
147 range: Range<usize>,
148) -> (Vec<T>, Range<usize>) {
149 if vec.is_empty() {
150 return (vec![], 0..0);
151 }
152
153 let start = range.start.min(vec.len() - 1);
154 let end = range.end.min(vec.len());
155
156 let return_range = start..end;
157
158 (vec[return_range.clone()].to_vec(), return_range)
159}