pub struct Page {
pub limit: i64,
pub offset: i64,
}Expand description
?limit=&offset= pagination for list endpoints, as an extractor.
limit defaults to DEFAULT_LIMIT (50) and is at
most MAX_LIMIT (100), to keep each request within the
free plan’s D1 rows-read budget; offset defaults to 0. Out-of-range or
non-numeric values are rejected with a JSON 400 (ApiError). Bind both
fields as LIMIT ?1 OFFSET ?2.
§Examples
use axum::extract::State;
use ocre::{ApiResult, Ctx, Json, Page, params};
async fn index(State(ctx): State<Ctx>, page: Page) -> ApiResult<Json<Vec<serde_json::Value>>> {
let sql = "SELECT * FROM posts ORDER BY id DESC LIMIT ?1 OFFSET ?2";
Ok(Json(ctx.db()?.all(sql, params![page.limit, page.offset]).await?))
}Fields§
§limit: i64Rows to return, 1..=100.
offset: i64Rows to skip, 0 or more.
Implementations§
Source§impl Page
impl Page
Sourcepub const DEFAULT_LIMIT: i64 = 50
pub const DEFAULT_LIMIT: i64 = 50
limit when the query string has none: 50.
Sourcepub fn new(limit: i64, offset: i64) -> Result<Self, Error>
pub fn new(limit: i64, offset: i64) -> Result<Self, Error>
Builds a page after checking the bounds: 1..=100 for limit, 0.. for offset.
The extractor calls it; call it yourself where there is no query string, e.g. GraphQL arguments.
§Errors
Error::BadRequest (400) when limit is outside 1..=100
(“limit must be between 1 and 100”) or offset is negative (“offset
must be 0 or more”).
§Examples
use ocre::Page;
assert_eq!(Page::new(20, 40).unwrap(), Page { limit: 20, offset: 40 });
assert_eq!(Page::new(101, 0).unwrap_err().to_string(), "bad request: limit must be between 1 and 100");
assert!(Page::new(10, -1).is_err());Sourcepub fn next(&self, returned: usize) -> Option<Self>
pub fn next(&self, returned: usize) -> Option<Self>
The page after this one, or None when returned (the rows this page got) is less than limit.
Ocre paginates without COUNT(*) (that query reads every row, and D1
bills rows read), so “is there more?” is guessed from a full page: when
the last page is exactly full, its “Next” link leads to an empty page.
§Examples
use ocre::Page;
let page = Page::new(50, 0).unwrap();
assert_eq!(page.next(50), Some(Page { limit: 50, offset: 50 }));
assert_eq!(page.next(12), None);Sourcepub fn previous(&self) -> Option<Self>
pub fn previous(&self) -> Option<Self>
The page before this one, or None on the first page.
§Examples
use ocre::Page;
assert_eq!(Page::new(50, 70).unwrap().previous(), Some(Page { limit: 50, offset: 20 }));
assert_eq!(Page::new(50, 0).unwrap().previous(), None);Sourcepub fn query(&self) -> String
pub fn query(&self) -> String
The query string of this page, without ?: offset=50, with limit= first when it is not the default.
Templates link to other pages with it:
{% if let Some(next) = page.next(posts.len()) %}<a href="?{{ next.query() }}">Next</a>{% endif %}.
§Examples
use ocre::Page;
assert_eq!(Page::new(50, 100).unwrap().query(), "offset=100");
assert_eq!(Page::new(20, 40).unwrap().query(), "limit=20&offset=40");Sourcepub fn links(&self, path: &str, returned: usize) -> PageLinks
pub fn links(&self, path: &str, returned: usize) -> PageLinks
Link header (RFC 8288) pointing JSON clients at the next, prev and first pages of path.
GitHub’s API convention. Returns a response part: put it before the
body in a tuple. returned is the number of rows this page got; see
next. No header on a single page.
§Examples
use axum::response::IntoResponse;
use ocre::{Json, Page};
let page = Page::new(2, 2).unwrap();
let posts = vec!["c", "d"];
let response = (page.links("/api/posts", posts.len()), Json(posts)).into_response();
assert_eq!(
response.headers()["link"],
r#"</api/posts?limit=2&offset=4>; rel="next", </api/posts?limit=2&offset=0>; rel="prev", </api/posts?limit=2&offset=0>; rel="first""#
);