use serde::{Deserialize, Serialize, Serializer};
use std::ops::{Deref, DerefMut};
use std::slice::{Iter, IterMut};
use std::vec::IntoIter;
use typed_builder::TypedBuilder;
#[cfg(feature = "fp-bindgen")]
use fp_bindgen::prelude::Serializable;
#[cfg(feature = "axum_06")]
use {
axum_06::http::{HeaderMap, HeaderValue},
axum_06::response::{IntoResponse, Response},
axum_06::Json,
};
pub const HAS_MORE_RESULTS_KEY: &str = "FP-Has-More-Results";
pub const TOTAL_RESULTS_KEY: &str = "FP-Total-Results";
#[derive(Debug, PartialEq)]
pub struct PagedVec<T> {
pub inner: Vec<T>,
pub has_more_results: bool,
pub total_results: Option<u32>,
}
impl<T> Deref for PagedVec<T> {
type Target = Vec<T>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> DerefMut for PagedVec<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T> PagedVec<T> {
pub fn page_amount(&self, pagination: &Pagination) -> Option<u32> {
if !self.has_more_results {
Some(pagination.page)
} else {
self.total_results.map(|results| results / pagination.limit)
}
}
#[inline]
pub fn iter(&self) -> Iter<'_, T> {
self.inner.iter()
}
#[inline]
pub fn iter_mut(&mut self) -> IterMut<'_, T> {
self.inner.iter_mut()
}
#[inline]
pub fn into_inner(self) -> Vec<T> {
self.inner
}
pub fn map<B, F>(self, f: F) -> PagedVec<B>
where
F: FnMut(T) -> B,
{
PagedVec {
inner: self.inner.into_iter().map(f).collect(),
has_more_results: self.has_more_results,
total_results: self.total_results,
}
}
#[inline]
pub fn into<B: From<T>>(self) -> PagedVec<B> {
self.map(|value| Into::<B>::into(value))
}
#[cfg(feature = "axum_06")]
pub fn unpack(self) -> (Vec<T>, HeaderMap) {
let has_more_results = &self.has_more_results.to_string();
let has_more_results = HeaderValue::from_str(has_more_results).unwrap();
let mut map = HeaderMap::new();
map.insert(HAS_MORE_RESULTS_KEY, has_more_results);
if let Some(total_results) = self.total_results {
let total_results = &total_results.to_string();
let total_results = HeaderValue::from_str(total_results).unwrap(); map.insert(TOTAL_RESULTS_KEY, total_results);
}
(self.inner, map)
}
}
#[cfg(feature = "axum_06")]
impl<T> IntoResponse for PagedVec<T>
where
T: Serialize,
Json<Vec<T>>: IntoResponse,
{
fn into_response(self) -> Response {
let (results, headers) = self.unpack();
(headers, Json(results)).into_response()
}
}
impl<T> IntoIterator for PagedVec<T> {
type Item = T;
type IntoIter = IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl<T: Serialize> Serialize for PagedVec<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.inner.serialize(serializer)
}
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, TypedBuilder)]
#[cfg_attr(
feature = "fp-bindgen",
derive(Serializable),
fp(rust_module = "fiberplane_models::paging")
)]
#[non_exhaustive]
pub struct Pagination {
#[serde(
default = "Pagination::default_page",
deserialize_with = "crate::deserialize_u32"
)]
pub page: u32,
#[serde(
default = "Pagination::default_limit",
deserialize_with = "crate::deserialize_u32"
)]
pub limit: u32,
}
impl Pagination {
#[inline]
fn default_page() -> u32 {
0
}
#[inline]
fn default_limit() -> u32 {
200
}
pub fn limit(&self) -> i64 {
self.limit as i64
}
pub fn offset(&self) -> i64 {
self.page as i64 * self.limit as i64
}
pub fn max() -> Self {
Self {
page: 0,
limit: u32::MAX,
}
}
}
impl Default for Pagination {
fn default() -> Self {
Self {
page: Pagination::default_page(),
limit: Pagination::default_limit(),
}
}
}