use async_trait::async_trait;
pub trait Query: Send + Sync {
type Result: Send + Sync;
}
#[async_trait]
pub trait QueryHandler<Q: Query>: Send + Sync {
type Error: std::error::Error + Send + Sync;
async fn handle(&self, query: Q) -> Result<Q::Result, Self::Error>;
}
#[async_trait]
pub trait QueryDispatcher: Send + Sync {
async fn dispatch<Q: Query>(
&self,
query: Q,
) -> Result<Q::Result, Box<dyn std::error::Error + Send + Sync>>;
}
pub trait CacheableQuery: Query {
fn cache_key(&self) -> String;
fn cache_ttl(&self) -> Option<u64> {
None }
}
pub trait PaginatedQuery: Query {
fn page(&self) -> u32;
fn page_size(&self) -> u32;
fn offset(&self) -> u32 {
(self.page().saturating_sub(1)) * self.page_size()
}
}
#[derive(Debug, Clone)]
pub struct PaginatedQueryResult<T> {
pub items: Vec<T>,
pub total: u64,
pub page: u32,
pub page_size: u32,
pub total_pages: u32,
}
impl<T> PaginatedQueryResult<T> {
pub fn new(items: Vec<T>, total: u64, page: u32, page_size: u32) -> Self {
let total_pages = if page_size > 0 {
((total as f64) / (page_size as f64)).ceil() as u32
} else {
0
};
Self {
items,
total,
page,
page_size,
total_pages,
}
}
pub fn has_next(&self) -> bool {
self.page < self.total_pages
}
pub fn has_previous(&self) -> bool {
self.page > 1
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestQuery {
id: String,
}
impl Query for TestQuery {
type Result = String;
}
struct TestHandler;
#[async_trait]
impl QueryHandler<TestQuery> for TestHandler {
type Error = std::io::Error;
async fn handle(&self, query: TestQuery) -> Result<String, Self::Error> {
Ok(format!("Result for {}", query.id))
}
}
#[tokio::test]
async fn test_query_handler() {
let handler = TestHandler;
let query = TestQuery {
id: "123".to_string(),
};
let result = handler.handle(query).await.unwrap();
assert_eq!(result, "Result for 123");
}
#[test]
fn test_paginated_result() {
let result: PaginatedQueryResult<i32> = PaginatedQueryResult::new(vec![1, 2, 3], 10, 1, 3);
assert_eq!(result.total_pages, 4);
assert!(result.has_next());
assert!(!result.has_previous());
}
}