use std::collections::HashMap;
use crate::filter::Filter;
use crate::pagination::Pagination;
use crate::types::OrderBy;
#[derive(Debug, Clone)]
pub struct IncludeSpec {
pub relation_name: String,
pub filter: Option<Filter>,
pub order_by: Option<OrderBy>,
pub pagination: Option<Pagination>,
pub nested: HashMap<String, IncludeSpec>,
pub include_count: bool,
}
impl IncludeSpec {
pub fn new(relation_name: impl Into<String>) -> Self {
Self {
relation_name: relation_name.into(),
filter: None,
order_by: None,
pagination: None,
nested: HashMap::new(),
include_count: false,
}
}
pub fn r#where(mut self, filter: impl Into<Filter>) -> Self {
self.filter = Some(filter.into());
self
}
pub fn order_by(mut self, order: impl Into<OrderBy>) -> Self {
self.order_by = Some(order.into());
self
}
pub fn skip(mut self, n: u64) -> Self {
self.pagination = Some(self.pagination.unwrap_or_default().skip(n));
self
}
pub fn take(mut self, n: u64) -> Self {
self.pagination = Some(self.pagination.unwrap_or_default().take(n));
self
}
pub fn include(mut self, nested: IncludeSpec) -> Self {
self.nested.insert(nested.relation_name.clone(), nested);
self
}
pub fn with_count(mut self) -> Self {
self.include_count = true;
self
}
pub fn has_nested(&self) -> bool {
!self.nested.is_empty()
}
pub fn nested_specs(&self) -> impl Iterator<Item = &IncludeSpec> {
self.nested.values()
}
}
#[derive(Debug, Clone, Default)]
pub struct Include {
specs: HashMap<String, IncludeSpec>,
}
impl Include {
pub fn new() -> Self {
Self::default()
}
pub fn with(mut self, spec: IncludeSpec) -> Self {
self.specs.insert(spec.relation_name.clone(), spec);
self
}
pub fn with_many(mut self, specs: impl IntoIterator<Item = IncludeSpec>) -> Self {
for spec in specs {
self.specs.insert(spec.relation_name.clone(), spec);
}
self
}
pub fn get(&self, relation: &str) -> Option<&IncludeSpec> {
self.specs.get(relation)
}
pub fn contains(&self, relation: &str) -> bool {
self.specs.contains_key(relation)
}
pub fn specs(&self) -> impl Iterator<Item = &IncludeSpec> {
self.specs.values()
}
pub fn is_empty(&self) -> bool {
self.specs.is_empty()
}
pub fn len(&self) -> usize {
self.specs.len()
}
pub fn merge(mut self, other: Include) -> Self {
self.specs.extend(other.specs);
self
}
}
impl From<IncludeSpec> for Include {
fn from(spec: IncludeSpec) -> Self {
Self::new().with(spec)
}
}
impl FromIterator<IncludeSpec> for Include {
fn from_iter<T: IntoIterator<Item = IncludeSpec>>(iter: T) -> Self {
Self::new().with_many(iter)
}
}
pub fn include(relation: impl Into<String>) -> IncludeSpec {
IncludeSpec::new(relation)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::OrderByField;
#[test]
fn test_include_spec_basic() {
let spec = IncludeSpec::new("posts");
assert_eq!(spec.relation_name, "posts");
assert!(spec.filter.is_none());
assert!(spec.order_by.is_none());
}
#[test]
fn test_include_spec_with_options() {
let spec = IncludeSpec::new("posts")
.order_by(OrderByField::desc("created_at"))
.take(5)
.with_count();
assert!(spec.order_by.is_some());
assert!(spec.pagination.is_some());
assert!(spec.include_count);
}
#[test]
fn test_include_spec_nested() {
let spec = IncludeSpec::new("posts").include(IncludeSpec::new("comments").take(10));
assert!(spec.has_nested());
assert!(spec.nested.contains_key("comments"));
}
#[test]
fn test_include_builder() {
let includes = Include::new()
.with(IncludeSpec::new("posts"))
.with(IncludeSpec::new("profile"));
assert_eq!(includes.len(), 2);
assert!(includes.contains("posts"));
assert!(includes.contains("profile"));
}
#[test]
fn test_include_from_iter() {
let includes: Include = vec![IncludeSpec::new("posts"), IncludeSpec::new("comments")]
.into_iter()
.collect();
assert_eq!(includes.len(), 2);
}
}