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}