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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! Pagination support for ViewSets
//!
//! Provides automatic pagination integration for list actions in ViewSets.
use async_trait::async_trait;
use reinhardt_core::pagination::{
CursorPagination, LimitOffsetPagination, PageNumberPagination, PaginatedResponse, Paginator,
};
use reinhardt_http::{Request, Result};
use serde::Serialize;
/// Pagination configuration for ViewSets
#[derive(Debug, Clone)]
pub enum PaginationConfig {
/// Page number based pagination (default: page_size=10, max_page_size=100)
PageNumber {
/// Number of items per page.
page_size: usize,
/// Maximum allowed page size.
max_page_size: Option<usize>,
},
/// Limit/offset based pagination (default: default_limit=10, max_limit=100)
LimitOffset {
/// Default number of items to return.
default_limit: usize,
/// Maximum allowed limit.
max_limit: Option<usize>,
},
/// Cursor based pagination for large datasets
Cursor {
/// Number of items per page.
page_size: usize,
/// Field name used for cursor ordering.
ordering_field: String,
},
/// No pagination - return all results
None,
}
impl Default for PaginationConfig {
fn default() -> Self {
Self::PageNumber {
page_size: 10,
max_page_size: Some(100),
}
}
}
impl PaginationConfig {
/// Create page number pagination with custom settings
///
/// # Examples
///
/// ```
/// use reinhardt_views::viewsets::PaginationConfig;
///
/// let config = PaginationConfig::page_number(20, Some(200));
/// ```
pub fn page_number(page_size: usize, max_page_size: Option<usize>) -> Self {
Self::PageNumber {
page_size,
max_page_size,
}
}
/// Create limit/offset pagination with custom settings
///
/// # Examples
///
/// ```
/// use reinhardt_views::viewsets::PaginationConfig;
///
/// let config = PaginationConfig::limit_offset(25, Some(500));
/// ```
pub fn limit_offset(default_limit: usize, max_limit: Option<usize>) -> Self {
Self::LimitOffset {
default_limit,
max_limit,
}
}
/// Create cursor pagination with custom settings
///
/// # Examples
///
/// ```
/// use reinhardt_views::viewsets::PaginationConfig;
///
/// let config = PaginationConfig::cursor(50, "created_at");
/// ```
pub fn cursor(page_size: usize, ordering_field: impl Into<String>) -> Self {
Self::Cursor {
page_size,
ordering_field: ordering_field.into(),
}
}
/// Disable pagination - return all results
///
/// # Examples
///
/// ```
/// use reinhardt_views::viewsets::PaginationConfig;
///
/// let config = PaginationConfig::none();
/// ```
pub fn none() -> Self {
Self::None
}
}
/// Trait for ViewSets that support pagination
#[async_trait]
pub trait PaginatedViewSet: Send + Sync {
/// Get pagination configuration for this ViewSet
///
/// Returns `None` to disable pagination, or a `PaginationConfig` to enable it.
fn get_pagination_config(&self) -> Option<PaginationConfig> {
Some(PaginationConfig::default())
}
/// Paginate a list of items based on the request and configuration
///
/// This method is automatically called by list actions when pagination is enabled.
async fn paginate_queryset<T: Serialize + Clone + Send + Sync>(
&self,
items: Vec<T>,
request: &Request,
) -> Result<PaginatedResponse<T>> {
let config = self.get_pagination_config().unwrap_or_default();
// Extract page parameter and base URL from request
let query_string = request.uri.query().unwrap_or("");
let base_url = request
.uri
.path_and_query()
.map(|pq| pq.path())
.unwrap_or("/");
match config {
PaginationConfig::PageNumber {
page_size,
max_page_size,
} => {
let mut paginator = PageNumberPagination::new().page_size(page_size);
if let Some(max) = max_page_size {
paginator = paginator.max_page_size(max);
}
paginator.paginate(&items, Some(query_string), base_url)
}
PaginationConfig::LimitOffset {
default_limit,
max_limit,
} => {
let mut paginator = LimitOffsetPagination::new().default_limit(default_limit);
if let Some(max) = max_limit {
paginator = paginator.max_limit(max);
}
paginator.paginate(&items, Some(query_string), base_url)
}
PaginationConfig::Cursor {
page_size,
ordering_field: _,
} => {
let paginator = CursorPagination::new().page_size(page_size);
paginator.paginate(&items, Some(query_string), base_url)
}
PaginationConfig::None => {
// No pagination - return all items
Ok(PaginatedResponse {
count: items.len(),
next: None,
previous: None,
results: items,
})
}
}
}
}