use serde::Deserialize;
#[derive(Debug, Clone, PartialEq)]
pub struct Page<T> {
pub data: Vec<T>,
pub count: u64,
pub total: Option<u64>,
}
impl<T> Page<T> {
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn iter(&self) -> std::slice::Iter<'_, T> {
self.data.iter()
}
}
impl<T> IntoIterator for Page<T> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.data.into_iter()
}
}
impl<'a, T> IntoIterator for &'a Page<T> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.data.iter()
}
}
impl<'de, T: Deserialize<'de>> Deserialize<'de> for Page<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum Envelope<T> {
Wrapped {
data: Vec<T>,
#[serde(default)]
count: Option<u64>,
#[serde(default)]
total: Option<u64>,
},
Bare(Vec<T>),
}
Ok(match Envelope::<T>::deserialize(deserializer)? {
Envelope::Wrapped { data, count, total } => {
let len = data.len() as u64;
Page {
data,
count: count.unwrap_or(len),
total,
}
}
Envelope::Bare(data) => {
let len = data.len() as u64;
Page {
data,
count: len,
total: None,
}
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn wrapped_envelope_with_count() {
let page: Page<serde_json::Value> =
serde_json::from_value(json!({"data": [{"id": 1}, {"id": 2}], "count": 2})).unwrap();
assert_eq!(page.len(), 2);
assert_eq!(page.count, 2);
assert_eq!(page.total, None);
}
#[test]
fn wrapped_envelope_with_total() {
let page: Page<serde_json::Value> =
serde_json::from_value(json!({"data": [{"id": 1}], "count": 1, "total": 41})).unwrap();
assert_eq!(page.count, 1);
assert_eq!(page.total, Some(41));
}
#[test]
fn bare_array_synthesizes_count() {
let page: Page<serde_json::Value> =
serde_json::from_value(json!([{"id": 1}, {"id": 2}, {"id": 3}])).unwrap();
assert_eq!(page.count, 3);
assert_eq!(page.total, None);
}
}