use super::{OffsetPageRequest, SortKey, SortOrder};
pub use super::KeysetDialect;
pub fn limit_offset(req: &OffsetPageRequest) -> (i64, i64) {
(req.limit() as i64, req.offset() as i64)
}
pub fn sort_clause(sort: &[SortKey], allowed_fields: &[&str]) -> String {
let parts: Vec<String> = sort
.iter()
.filter(|k| allowed_fields.contains(&k.field.as_str()))
.map(|k| {
let dir = match k.order {
SortOrder::Asc => "ASC",
SortOrder::Desc => "DESC",
};
format!("{} {}", k.field, dir)
})
.collect();
if parts.is_empty() { String::new() } else { format!(" ORDER BY {}", parts.join(", ")) }
}
pub fn keyset_where_clause(
sort: &[SortKey],
start_param: usize,
dialect: KeysetDialect,
) -> Option<String> {
if sort.is_empty() {
return None;
}
let cols: Vec<&str> = sort.iter().map(|k| k.field.as_str()).collect();
let params: Vec<String> = (0..sort.len())
.map(|i| match dialect {
KeysetDialect::Positional => format!("${}", start_param + i),
KeysetDialect::Question => "?".to_owned(),
})
.collect();
Some(format!("({}) > ({})", cols.join(", "), params.join(", ")))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pagination::{OffsetPageRequest, SortKey};
#[test]
fn limit_offset_page1() {
let req = OffsetPageRequest::new(1, 20).unwrap();
assert_eq!(limit_offset(&req), (20, 0));
}
#[test]
fn limit_offset_page3() {
let req = OffsetPageRequest::new(3, 10).unwrap();
assert_eq!(limit_offset(&req), (10, 20));
}
#[test]
fn sort_clause_single_asc() {
let s = sort_clause(&[SortKey::asc("name")], &["name", "id"]);
assert_eq!(s, " ORDER BY name ASC");
}
#[test]
fn sort_clause_multi_mixed() {
let s =
sort_clause(&[SortKey::asc("created_at"), SortKey::desc("id")], &["created_at", "id"]);
assert_eq!(s, " ORDER BY created_at ASC, id DESC");
}
#[test]
fn sort_clause_filters_unknown_fields() {
let s = sort_clause(&[SortKey::asc("evil; DROP TABLE users")], &["name"]);
assert_eq!(s, "");
}
#[test]
fn sort_clause_empty_returns_empty_string() {
assert_eq!(sort_clause(&[], &["id"]), "");
}
#[test]
fn keyset_where_positional() {
let clause = keyset_where_clause(
&[SortKey::asc("created_at"), SortKey::asc("id")],
1,
KeysetDialect::Positional,
);
assert_eq!(clause, Some("(created_at, id) > ($1, $2)".to_owned()));
}
#[test]
fn keyset_where_question_mark() {
let clause = keyset_where_clause(&[SortKey::asc("id")], 1, KeysetDialect::Question);
assert_eq!(clause, Some("(id) > (?)".to_owned()));
}
#[test]
fn keyset_where_offset_start_param() {
let clause = keyset_where_clause(
&[SortKey::asc("a"), SortKey::desc("b")],
3,
KeysetDialect::Positional,
);
assert_eq!(clause, Some("(a, b) > ($3, $4)".to_owned()));
}
#[test]
fn keyset_where_empty_sort_is_none() {
assert_eq!(keyset_where_clause(&[], 1, KeysetDialect::Positional), None);
}
}