Skip to main content

Page

Struct Page 

Source
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: i64

Rows to return, 1..=100.

§offset: i64

Rows to skip, 0 or more.

Implementations§

Source§

impl Page

Source

pub const DEFAULT_LIMIT: i64 = 50

limit when the query string has none: 50.

Source

pub const MAX_LIMIT: i64 = 100

Largest accepted limit: 100.

Source

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());
Source

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);
Source

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);
Source

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");

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""#
);

Trait Implementations§

Source§

impl Clone for Page

Source§

fn clone(&self) -> Page

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for Page

Source§

impl Debug for Page

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<S: Send + Sync> FromRequestParts<S> for Page

Source§

type Rejection = ApiError

If the extractor fails it’ll use this “rejection” type. A rejection is a kind of error that can be converted into a response.
Source§

async fn from_request_parts( parts: &mut Parts, state: &S, ) -> Result<Self, Self::Rejection>

Perform the extraction.
Source§

impl PartialEq for Page

Source§

fn eq(&self, other: &Page) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl StructuralPartialEq for Page

Auto Trait Implementations§

§

impl Freeze for Page

§

impl RefUnwindSafe for Page

§

impl Send for Page

§

impl Sync for Page

§

impl Unpin for Page

§

impl UnsafeUnpin for Page

§

impl UnwindSafe for Page

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> FromRef<T> for T
where T: Clone,

§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
§

impl<S, T> FromRequest<S, ViaParts> for T
where S: Send + Sync, T: FromRequestParts<S>,

§

type Rejection = <T as FromRequestParts<S>>::Rejection

If the extractor fails it’ll use this “rejection” type. A rejection is a kind of error that can be converted into a response.
§

fn from_request( req: Request<Body>, state: &S, ) -> impl Future<Output = Result<T, <T as FromRequest<S, ViaParts>>::Rejection>>

Perform the extraction.
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<S, T> Upcast<T> for S
where T: UpcastFrom<S> + ?Sized, S: ?Sized,

Source§

fn upcast(&self) -> &T
where Self: ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider ref type within the Wasm bindgen generics type system. Read more
Source§

fn upcast_into(self) -> T
where Self: Sized + ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider type within the Wasm bindgen generics type system. Read more
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V