use orbital_data::DataValue;
use serde::{Deserialize, Serialize};
#[cfg(feature = "leptos")]
mod infinite_scroll;
#[cfg(feature = "leptos")]
pub use infinite_scroll::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SortDirectionWire {
Asc,
Desc,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SortParam {
pub field: String,
pub direction: SortDirectionWire,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum FilterLogicWire {
#[default]
And,
Or,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FilterRuleParam {
pub field: String,
pub operator: String,
pub value: DataValue,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FilterQuery {
pub items: Vec<FilterRuleParam>,
pub logic: FilterLogicWire,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PageRequest {
pub offset: u32,
pub limit: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sort: Option<Vec<SortParam>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub filter: Option<FilterQuery>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub quick_search: Option<String>,
}
impl PageRequest {
pub fn new(offset: u32, limit: u32) -> Self {
Self {
offset,
limit,
sort: None,
filter: None,
quick_search: None,
}
}
pub fn with_query(
offset: u32,
limit: u32,
sort: Option<Vec<SortParam>>,
filter: Option<FilterQuery>,
quick_search: Option<String>,
) -> Self {
Self {
offset,
limit,
sort,
filter,
quick_search,
}
}
pub fn is_first_page(&self) -> bool {
self.offset == 0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Page<T> {
pub items: Vec<T>,
pub has_more: bool,
pub total_count: Option<u64>,
#[serde(default)]
pub next_request_offset: Option<u32>,
}
impl<T> Page<T> {
pub fn from_oversized(mut raw_items: Vec<T>, limit: u32, total_count: Option<u64>) -> Self {
let has_more = raw_items.len() as u32 > limit;
if has_more {
raw_items.truncate(limit as usize);
}
Self {
items: raw_items,
has_more,
total_count,
next_request_offset: None,
}
}
pub fn empty() -> Self {
Self {
items: Vec::new(),
has_more: false,
total_count: Some(0),
next_request_offset: None,
}
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_oversized_trims_and_sets_has_more() {
let items: Vec<u32> = (1..=11).collect(); let page = Page::from_oversized(items, 10, Some(25));
assert_eq!(page.items.len(), 10);
assert!(page.has_more);
assert_eq!(page.total_count, Some(25));
}
#[test]
fn from_oversized_no_extra_item() {
let items: Vec<u32> = (1..=10).collect(); let page = Page::from_oversized(items, 10, None);
assert_eq!(page.items.len(), 10);
assert!(!page.has_more);
assert_eq!(page.total_count, None);
}
#[test]
fn page_request_first_page() {
assert!(PageRequest::new(0, 10).is_first_page());
assert!(!PageRequest::new(10, 10).is_first_page());
}
#[test]
fn page_request_legacy_deserialize() {
let json = r#"{"offset":0,"limit":10}"#;
let req: PageRequest = serde_json::from_str(json).unwrap();
assert_eq!(req.offset, 0);
assert_eq!(req.limit, 10);
assert!(req.sort.is_none());
assert!(req.filter.is_none());
assert!(req.quick_search.is_none());
}
#[test]
fn empty_page() {
let page: Page<String> = Page::empty();
assert!(page.is_empty());
assert!(!page.has_more);
assert_eq!(page.total_count, Some(0));
}
}