leptos_struct_table/
data_provider.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#![allow(async_fn_in_trait)]

use crate::ColumnSort;
use std::collections::VecDeque;
use std::fmt::Debug;
use std::ops::Range;

/// The trait that provides data for the `<TableContent>` component.
/// Anything that is passed to the `rows` prop must implement this trait.
///
/// If you add `#[table(impl_vec_data_provider)]` to your row struct,
/// this is automatically implemented for `Vec<Row>`.
/// This way a simple list of items can be passed to the table.
///
/// This is also automatically implemented for any struct that implements
/// [`PaginatedTableDataProvider`] or [`ExactTableDataProvider`].
/// The first is a more convenient way of connecting to a paginated data source and the second is
/// more convenient if you know you're always going to return exactly the requested range (except maybe
/// at the end of the data).
pub trait TableDataProvider<Row, Err: Debug = String> {
    /// If Some(...), data will be loaded in chunks of this size. This is useful for paginated data sources.
    /// If you have such a paginated data source, you probably want to implement `PaginatedTableDataProvider`
    /// instead of this trait.
    const CHUNK_SIZE: Option<usize> = None;

    /// Get all data rows for the table specified by the range. This method is called when the table is rendered.
    /// The range is determined by the visible rows and used to virtualize the table.
    /// The parameter `range` is only determined by visibility and may be out of bounds. It is the
    /// responsibility of the implementation to handle this case. Use [get_vec_range_clamped] to get a
    /// range that is clamped to the length of the vector.
    ///
    /// It returns a `Vec` of all rows loaded and the range that these rows cover. Depending on
    /// the data source you might not be able to load exactly the requested range; that's why
    /// the actual loaded range is returned in addition to the rows. You should always return
    /// at least the range that is requested or more. If you return less rows than requested,
    /// it is assumed that the data source is done and there are no more rows to load.
    ///
    /// In the case of an error the returned error `String` is going to be displayed in a
    /// in place of the failed rows.
    async fn get_rows(&self, range: Range<usize>) -> Result<(Vec<Row>, Range<usize>), Err>;

    /// The total number of rows in the table. Returns `None` if unknown (which is the default).
    async fn row_count(&self) -> Option<usize> {
        None
    }

    /// Set the sorting of the table. The sorting is a list of column names and the sort order sorted by priority.
    /// The first entry in the list is the most important one.
    /// The default implementation does nothing.
    /// For example: `[(0, ColumnSort::Ascending), (1, ColumnSort::Descending)]`
    /// will sort by name first and then by age.
    /// Please note that after calling this method, data will be reloaded through [`get_rows`](TableDataProvider::get_rows).
    #[allow(unused_variables)]
    fn set_sorting(&mut self, sorting: &VecDeque<(usize, ColumnSort)>) {
        // by default do nothing
    }

    /// Call `.track()` in this method on all signals that loading data relies on.
    /// 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)
    fn track(&self) {
        // by default do nothing
    }
}

/// A paginated data source. This is meant to provide a more convenient way
/// of connecting to a paginated data source instead of implementing [`TableDataProvider`] directly.
///
/// If you implement this for your struct, [`TableDataProvider`] is automatically implemented for you.
///
/// > Please note that this is independent from using [`DisplayStrategy::Pagination`] with [`TableContent`].
/// > You do not have implement this trait if you're using pagination and you vice versa if you're not using pagination
/// > you can still implement this trait. And in case if you use this trait together with pagination the
/// > display row count can be different from the `PAGE_ROW_COUNT`.
pub trait PaginatedTableDataProvider<Row, Err: Debug = String> {
    /// How many rows per page
    const PAGE_ROW_COUNT: usize;

    /// Get all data rows for the table specified by the page index (starts a 0).
    ///
    /// If you return less than `PAGE_ROW_COUNT` rows, it is assumed that the end of the
    /// data has been reached.
    async fn get_page(&self, page_index: usize) -> Result<Vec<Row>, Err>;

    /// The total number of rows in the table. Returns `None` if unknown (which is the default).
    ///
    /// By default this is computed from the [`page_count`] method. But if your data source
    /// tells you the number of rows instead of the number of pages you should override this method.
    async fn row_count(&self) -> Option<usize> {
        self.page_count().await.map(|pc| pc * Self::PAGE_ROW_COUNT)
    }

    /// The total number of pages in the data source. Returns `None` if unknown (which is the default).
    ///
    /// If your data source gives you the number of rows instead of the number of pages
    /// you should implement [`row_count`] instead of this method.
    async fn page_count(&self) -> Option<usize> {
        None
    }

    /// Same as [`TableDataProvider::set_sorting`]
    #[allow(unused_variables)]
    fn set_sorting(&mut self, sorting: &VecDeque<(usize, ColumnSort)>) {
        // by default do nothing
    }

    /// Same as [`TableDataProvider::track`]
    fn track(&self) {
        // by default do nothing
    }
}

impl<Row, Err, D> TableDataProvider<Row, Err> for D
where
    D: PaginatedTableDataProvider<Row, Err>,
    Err: Debug,
{
    const CHUNK_SIZE: Option<usize> = Some(D::PAGE_ROW_COUNT);

    async fn get_rows(&self, range: Range<usize>) -> Result<(Vec<Row>, Range<usize>), Err> {
        let Range { start, end } = range;

        debug_assert_eq!(start % D::PAGE_ROW_COUNT, 0);
        debug_assert_eq!(end - start, D::PAGE_ROW_COUNT);

        self.get_page(start / D::PAGE_ROW_COUNT).await.map(|rows| {
            let len = rows.len();
            (rows, start..start + len)
        })
    }

    async fn row_count(&self) -> Option<usize> {
        PaginatedTableDataProvider::<Row, Err>::row_count(self).await
    }

    fn set_sorting(&mut self, sorting: &VecDeque<(usize, ColumnSort)>) {
        PaginatedTableDataProvider::<Row, Err>::set_sorting(self, sorting)
    }

    fn track(&self) {
        PaginatedTableDataProvider::<Row, Err>::track(self)
    }
}

/// Return `vec[range.start..range.end]` where `range` is clamped to the length of `vec`.
pub fn get_vec_range_clamped<T: Clone>(vec: &[T], range: Range<usize>) -> (Vec<T>, Range<usize>) {
    if vec.is_empty() {
        return (vec![], 0..0);
    }

    let start = range.start.min(vec.len() - 1);
    let end = range.end.min(vec.len());

    let return_range = start..end;

    (vec[return_range.clone()].to_vec(), return_range)
}