Refactor pagination

This commit is contained in:
Magnus Hoff 2017-10-20 20:48:38 +02:00
parent 29c4180496
commit d918d614b4
3 changed files with 65 additions and 38 deletions

View file

@ -13,12 +13,12 @@ use super::pagination::Pagination;
pub struct ChangesResource {
state: State,
before: Option<i32>,
pagination: Pagination<i32>,
}
impl ChangesResource {
pub fn new(state: State, pagination: Pagination<i32>) -> Self {
Self { state, before: match pagination { Pagination::Before(x) => Some(x), _ => None } }
Self { state, pagination }
}
}
@ -60,27 +60,55 @@ impl Resource for ChangesResource {
const PAGE_SIZE: i32 = 30;
let data = self.state.get_article_revision_stubs(self.before, PAGE_SIZE);
let pagination = self.pagination.clone();
let data = self.state.query_article_revision_stubs(move |query| {
use diesel::prelude::*;
use schema::article_revisions::dsl::*;
let query = query
.limit(PAGE_SIZE as i64 + 1);
match pagination {
Pagination::After(x) => query
.filter(sequence_number.gt(x))
.order(sequence_number.asc()),
Pagination::Before(x) => query
.filter(sequence_number.lt(x))
.order(sequence_number.desc()),
Pagination::None => query
.order(sequence_number.desc()),
}
});
let head = self.head();
Box::new(data.join(head)
.and_then(move |(data, head)| {
.and_then(move |(mut data, head)| {
use std::iter::Iterator;
let link_newer = self.before.and_then(|_| {
data.first().and_then(|x| {
match x.sequence_number {
seq => Some(format!("?before={}", seq + PAGE_SIZE)),
}
})
});
let extra_element = if data.len() > PAGE_SIZE as usize {
data.pop()
} else {
None
};
let link_older = data.last().and_then(|x| {
match x.sequence_number {
1 => None,
seq => Some(format!("?before={}", seq)),
}
});
let (link_newer, link_older) = match self.pagination {
Pagination::After(x) => {
data.reverse();
(
extra_element.map(|_| format!("?after={}", data.first().unwrap().sequence_number)),
Some(format!("?before={}", x + 1))
)
},
Pagination::Before(x) => (
Some(format!("?after={}", x - 1)),
extra_element.map(|_| format!("?before={}", data.last().unwrap().sequence_number)),
),
Pagination::None => (
None,
extra_element.map(|_| format!("?before={}", data.last().unwrap().sequence_number)),
),
};
let changes = &data.into_iter().map(|x| {
Row {

View file

@ -24,6 +24,7 @@ struct PaginationStruct<T> {
before: Option<T>,
}
#[derive(Clone)]
pub enum Pagination<T> {
After(T),
Before(T),

View file

@ -101,33 +101,31 @@ impl State {
})
}
pub fn get_article_revision_stubs(&self, before: Option<i32>, limit: i32) -> CpuFuture<Vec<models::ArticleRevisionStub>, Error> {
pub fn query_article_revision_stubs<F>(&self, f: F) -> CpuFuture<Vec<models::ArticleRevisionStub>, Error>
where
F: 'static + Send + Sync,
for <'a> F:
FnOnce(article_revisions::BoxedQuery<'a, diesel::sqlite::Sqlite>) ->
article_revisions::BoxedQuery<'a, diesel::sqlite::Sqlite>,
{
let connection_pool = self.connection_pool.clone();
self.cpu_pool.spawn_fn(move || {
use schema::article_revisions;
use schema::article_revisions::dsl::*;
let query = article_revisions::table
.order(article_revisions::sequence_number.desc())
.limit(limit as i64)
Ok(f(article_revisions.into_boxed())
.select((
article_revisions::sequence_number,
article_revisions::article_id,
article_revisions::revision,
article_revisions::created,
article_revisions::slug,
article_revisions::title,
article_revisions::latest,
article_revisions::author,
sequence_number,
article_id,
revision,
created,
slug,
title,
latest,
author,
))
.into_boxed();
let query = match before {
Some(before) => query.filter(article_revisions::sequence_number.lt(before)),
None => query
};
Ok(query.load(&*connection_pool.get()?)?)
.load(&*connection_pool.get()?)?
)
})
}