diff --git a/src/resources/changes_resource.rs b/src/resources/changes_resource.rs index e50a38f..f8b464a 100644 --- a/src/resources/changes_resource.rs +++ b/src/resources/changes_resource.rs @@ -13,12 +13,12 @@ use super::pagination::Pagination; pub struct ChangesResource { state: State, - before: Option, + pagination: Pagination, } impl ChangesResource { pub fn new(state: State, pagination: Pagination) -> 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 { diff --git a/src/resources/pagination.rs b/src/resources/pagination.rs index de56967..8ed8fe9 100644 --- a/src/resources/pagination.rs +++ b/src/resources/pagination.rs @@ -24,6 +24,7 @@ struct PaginationStruct { before: Option, } +#[derive(Clone)] pub enum Pagination { After(T), Before(T), diff --git a/src/state.rs b/src/state.rs index cb4518a..0c7b7c1 100644 --- a/src/state.rs +++ b/src/state.rs @@ -101,33 +101,31 @@ impl State { }) } - pub fn get_article_revision_stubs(&self, before: Option, limit: i32) -> CpuFuture, Error> { + pub fn query_article_revision_stubs(&self, f: F) -> CpuFuture, 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()?)? + ) }) }