Skip to main content

reinhardt_views/viewsets/
pagination_support.rs

1//! Pagination support for ViewSets
2//!
3//! Provides automatic pagination integration for list actions in ViewSets.
4
5use async_trait::async_trait;
6use reinhardt_core::pagination::{
7	CursorPagination, LimitOffsetPagination, PageNumberPagination, PaginatedResponse, Paginator,
8};
9use reinhardt_http::{Request, Result};
10use serde::Serialize;
11
12/// Pagination configuration for ViewSets
13#[derive(Debug, Clone)]
14pub enum PaginationConfig {
15	/// Page number based pagination (default: page_size=10, max_page_size=100)
16	PageNumber {
17		/// Number of items per page.
18		page_size: usize,
19		/// Maximum allowed page size.
20		max_page_size: Option<usize>,
21	},
22	/// Limit/offset based pagination (default: default_limit=10, max_limit=100)
23	LimitOffset {
24		/// Default number of items to return.
25		default_limit: usize,
26		/// Maximum allowed limit.
27		max_limit: Option<usize>,
28	},
29	/// Cursor based pagination for large datasets
30	Cursor {
31		/// Number of items per page.
32		page_size: usize,
33		/// Field name used for cursor ordering.
34		ordering_field: String,
35	},
36	/// No pagination - return all results
37	None,
38}
39
40impl Default for PaginationConfig {
41	fn default() -> Self {
42		Self::PageNumber {
43			page_size: 10,
44			max_page_size: Some(100),
45		}
46	}
47}
48
49impl PaginationConfig {
50	/// Create page number pagination with custom settings
51	///
52	/// # Examples
53	///
54	/// ```
55	/// use reinhardt_views::viewsets::PaginationConfig;
56	///
57	/// let config = PaginationConfig::page_number(20, Some(200));
58	/// ```
59	pub fn page_number(page_size: usize, max_page_size: Option<usize>) -> Self {
60		Self::PageNumber {
61			page_size,
62			max_page_size,
63		}
64	}
65
66	/// Create limit/offset pagination with custom settings
67	///
68	/// # Examples
69	///
70	/// ```
71	/// use reinhardt_views::viewsets::PaginationConfig;
72	///
73	/// let config = PaginationConfig::limit_offset(25, Some(500));
74	/// ```
75	pub fn limit_offset(default_limit: usize, max_limit: Option<usize>) -> Self {
76		Self::LimitOffset {
77			default_limit,
78			max_limit,
79		}
80	}
81
82	/// Create cursor pagination with custom settings
83	///
84	/// # Examples
85	///
86	/// ```
87	/// use reinhardt_views::viewsets::PaginationConfig;
88	///
89	/// let config = PaginationConfig::cursor(50, "created_at");
90	/// ```
91	pub fn cursor(page_size: usize, ordering_field: impl Into<String>) -> Self {
92		Self::Cursor {
93			page_size,
94			ordering_field: ordering_field.into(),
95		}
96	}
97
98	/// Disable pagination - return all results
99	///
100	/// # Examples
101	///
102	/// ```
103	/// use reinhardt_views::viewsets::PaginationConfig;
104	///
105	/// let config = PaginationConfig::none();
106	/// ```
107	pub fn none() -> Self {
108		Self::None
109	}
110}
111
112/// Trait for ViewSets that support pagination
113#[async_trait]
114pub trait PaginatedViewSet: Send + Sync {
115	/// Get pagination configuration for this ViewSet
116	///
117	/// Returns `None` to disable pagination, or a `PaginationConfig` to enable it.
118	fn get_pagination_config(&self) -> Option<PaginationConfig> {
119		Some(PaginationConfig::default())
120	}
121
122	/// Paginate a list of items based on the request and configuration
123	///
124	/// This method is automatically called by list actions when pagination is enabled.
125	async fn paginate_queryset<T: Serialize + Clone + Send + Sync>(
126		&self,
127		items: Vec<T>,
128		request: &Request,
129	) -> Result<PaginatedResponse<T>> {
130		let config = self.get_pagination_config().unwrap_or_default();
131
132		// Extract page parameter and base URL from request
133		let query_string = request.uri.query().unwrap_or("");
134		let base_url = request
135			.uri
136			.path_and_query()
137			.map(|pq| pq.path())
138			.unwrap_or("/");
139
140		match config {
141			PaginationConfig::PageNumber {
142				page_size,
143				max_page_size,
144			} => {
145				let mut paginator = PageNumberPagination::new().page_size(page_size);
146				if let Some(max) = max_page_size {
147					paginator = paginator.max_page_size(max);
148				}
149				paginator.paginate(&items, Some(query_string), base_url)
150			}
151			PaginationConfig::LimitOffset {
152				default_limit,
153				max_limit,
154			} => {
155				let mut paginator = LimitOffsetPagination::new().default_limit(default_limit);
156				if let Some(max) = max_limit {
157					paginator = paginator.max_limit(max);
158				}
159				paginator.paginate(&items, Some(query_string), base_url)
160			}
161			PaginationConfig::Cursor {
162				page_size,
163				ordering_field: _,
164			} => {
165				let paginator = CursorPagination::new().page_size(page_size);
166				paginator.paginate(&items, Some(query_string), base_url)
167			}
168			PaginationConfig::None => {
169				// No pagination - return all items
170				Ok(PaginatedResponse {
171					count: items.len(),
172					next: None,
173					previous: None,
174					results: items,
175				})
176			}
177		}
178	}
179}