leptos_struct_table/
loaded_rows.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
use leptos::prelude::*;
use std::ops::{Index, Range};

pub enum RowState<T: Send + Sync + 'static> {
    /// The row is not yet loaded and a placeholder is displayed if the row is visible in the viewport.
    Placeholder,
    /// The row is loading and a placeholder is displayed if the row is visible in the viewport.
    Loading,
    /// The row has been loaded.
    Loaded(RwSignal<T>),
    /// The row failed to load. This error is shown in the row if it's visible in the viewport.
    Error(String),
}

impl<T: Send + Sync + 'static> Clone for RowState<T> {
    fn clone(&self) -> Self {
        match self {
            RowState::Placeholder => RowState::Placeholder,
            RowState::Loading => RowState::Loading,
            RowState::Loaded(signal) => RowState::Loaded(*signal),
            RowState::Error(error) => RowState::Error(error.clone()),
        }
    }
}

impl<T: Send + Sync + 'static> std::fmt::Debug for RowState<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            RowState::Placeholder => write!(f, "Placeholder"),
            RowState::Loading => write!(f, "Loading"),
            RowState::Loaded(_) => write!(f, "Loaded"),
            RowState::Error(e) => write!(f, "Error({})", e),
        }
    }
}

/// This is basically a cache for rows and used by [`TableContent`] internally to track
/// which rows are already loaded, which are still loading and which are missing.
pub struct LoadedRows<T: Send + Sync + 'static> {
    rows: Vec<RowState<T>>,
}

impl<T: Send + Sync + 'static> LoadedRows<T> {
    pub fn new() -> Self {
        Self { rows: vec![] }
    }

    #[inline]
    pub fn len(&self) -> usize {
        self.rows.len()
    }

    #[inline]
    pub fn resize(&mut self, len: usize) {
        self.rows.resize(len, RowState::Placeholder);
    }

    pub fn write_loading(&mut self, range: Range<usize>) {
        if range.end > self.rows.len() {
            self.rows.resize(range.end, RowState::Placeholder);
        }

        for row in &mut self.rows[range] {
            *row = RowState::Loading;
        }
    }

    pub fn write_loaded(
        &mut self,
        loading_result: Result<(Vec<T>, Range<usize>), String>,
        missing_range: Range<usize>,
    ) {
        match loading_result {
            Ok((rows, range)) => {
                if range.end > self.rows.len() {
                    self.rows.resize(range.end, RowState::Placeholder);
                }

                for (self_row, loaded_row) in self.rows[range].iter_mut().zip(rows) {
                    *self_row = RowState::Loaded(RwSignal::new(loaded_row));
                }
            }
            Err(error) => {
                let range = missing_range.start..missing_range.end.min(self.rows.len());
                if range.start >= range.end {
                    return;
                }

                for row in &mut self.rows[range] {
                    *row = RowState::Error(error.clone());
                }
            }
        }
    }

    #[inline]
    pub fn missing_range(&self, range: Range<usize>) -> Option<Range<usize>> {
        let do_load_predicate = |row| matches!(row, &RowState::Placeholder);

        let slice = &self.rows[range.clone()];

        let start = slice.iter().position(do_load_predicate)?;
        let end = slice.iter().rposition(do_load_predicate)?;

        let start = start + range.start;
        let end = end + range.start + 1;

        Some(start..end)
    }

    #[inline]
    pub fn clear(&mut self) {
        self.rows.fill(RowState::Placeholder);
    }
}

impl<T: Sync + Send> Index<Range<usize>> for LoadedRows<T> {
    type Output = [RowState<T>];

    #[inline]
    fn index(&self, index: Range<usize>) -> &Self::Output {
        &self.rows[index]
    }
}

impl<T: Send + Sync> Index<usize> for LoadedRows<T> {
    type Output = RowState<T>;

    #[inline]
    fn index(&self, index: usize) -> &Self::Output {
        &self.rows[index]
    }
}