finnow/finnow-api/source/util/pagination.d

110 lines
2.7 KiB
D

/**
* Defines various components useful for paginated operations.
*/
module util.pagination;
import handy_http_primitives;
import std.conv;
enum SortDir : string {
ASC = "ASC",
DESC = "DESC"
}
struct Sort {
immutable string attribute;
immutable SortDir dir;
static Optional!Sort parse(string expr) {
import std.string;
string[] parts = expr.split(",");
if (parts.length == 1) return Optional!Sort.of(Sort(parts[0], SortDir.ASC));
if (parts.length != 2) return Optional!Sort.empty;
string attr = parts[0];
string dirExpr = parts[1];
SortDir d;
if (dirExpr == SortDir.ASC) {
d = SortDir.ASC;
} else if (dirExpr == SortDir.DESC) {
d = SortDir.DESC;
} else {
return Optional!Sort.empty;
}
return Optional!Sort.of(Sort(attr, d));
}
}
struct PageRequest {
immutable uint page;
immutable ushort size;
immutable Sort[] sorts;
bool isUnpaged() const {
return page < 1;
}
static PageRequest unpaged() {
return PageRequest(0, 0, []);
}
static PageRequest parse(in ServerHttpRequest request, PageRequest defaults) {
import std.algorithm;
import std.array;
uint pg = request.getParamAs!uint("page", defaults.page);
ushort sz = request.getParamAs!ushort("size", defaults.size);
Sort[] s = request.queryParams
.filter!(p => p.key == "sort" && p.values.length > 0)
.map!(p => Sort.parse(p.values[0]))
.filter!(o => !o.isNull)
.map!(o => o.value)
.array;
return PageRequest(pg, sz, s.idup);
}
string toSql() const {
import std.array;
auto app = appender!string;
if (sorts.length > 0) {
app ~= "ORDER BY ";
for (size_t i = 0; i < sorts.length; i++) {
app ~= sorts[i].attribute;
app ~= " ";
app ~= cast(string) sorts[i].dir;
if (i + 1 < sorts.length) app ~= ",";
}
app ~= " ";
}
app ~= "LIMIT ";
app ~= size.to!string;
app ~= " OFFSET ";
app ~= page.to!string;
return app[];
}
PageRequest next() const {
if (isUnpaged) return this;
return PageRequest(page + 1, size, sorts);
}
PageRequest prev() const {
if (isUnpaged) return this;
return PageRequest(page - 1, size, sorts);
}
}
struct Page(T) {
T[] items;
PageRequest pageRequest;
}
Page!U mapItems(T, U)(in Page!T page, U function(const(T)) fn) {
import std.algorithm : map;
import std.array : array;
return Page!U(
page.items.map!fn.array,
page.pageRequest
);
}