prax_query/middleware/
types.rs1use crate::QueryError;
4use std::future::Future;
5use std::pin::Pin;
6use std::sync::Arc;
7
8use super::context::QueryContext;
9
10pub type MiddlewareResult<T> = Result<T, QueryError>;
12
13pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
15
16pub struct Next<'a> {
20 pub(crate) inner: Box<dyn FnOnce(QueryContext) -> BoxFuture<'a, MiddlewareResult<QueryResponse>> + Send + 'a>,
21}
22
23impl<'a> Next<'a> {
24 pub fn run(self, ctx: QueryContext) -> BoxFuture<'a, MiddlewareResult<QueryResponse>> {
26 (self.inner)(ctx)
27 }
28}
29
30#[derive(Debug, Clone)]
32pub struct QueryResponse {
33 pub data: serde_json::Value,
35 pub rows_affected: Option<u64>,
37 pub execution_time_us: u64,
39 pub from_cache: bool,
41 pub metadata: serde_json::Map<String, serde_json::Value>,
43}
44
45impl QueryResponse {
46 pub fn new(data: serde_json::Value) -> Self {
48 Self {
49 data,
50 rows_affected: None,
51 execution_time_us: 0,
52 from_cache: false,
53 metadata: serde_json::Map::new(),
54 }
55 }
56
57 pub fn empty() -> Self {
59 Self::new(serde_json::Value::Null)
60 }
61
62 pub fn with_affected(count: u64) -> Self {
64 Self {
65 data: serde_json::Value::Null,
66 rows_affected: Some(count),
67 execution_time_us: 0,
68 from_cache: false,
69 metadata: serde_json::Map::new(),
70 }
71 }
72
73 pub fn with_execution_time(mut self, us: u64) -> Self {
75 self.execution_time_us = us;
76 self
77 }
78
79 pub fn from_cache(mut self) -> Self {
81 self.from_cache = true;
82 self
83 }
84
85 pub fn with_metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
87 self.metadata.insert(key.into(), value);
88 self
89 }
90}
91
92pub trait Middleware: Send + Sync {
129 fn handle<'a>(
131 &'a self,
132 ctx: QueryContext,
133 next: Next<'a>,
134 ) -> BoxFuture<'a, MiddlewareResult<QueryResponse>>;
135
136 fn name(&self) -> &'static str {
138 std::any::type_name::<Self>()
139 }
140
141 fn enabled(&self) -> bool {
143 true
144 }
145}
146
147pub type SharedMiddleware = Arc<dyn Middleware>;
149
150pub trait IntoSharedMiddleware {
152 fn into_shared(self) -> SharedMiddleware;
154}
155
156impl<T: Middleware + 'static> IntoSharedMiddleware for T {
157 fn into_shared(self) -> SharedMiddleware {
158 Arc::new(self)
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn test_query_response_builder() {
168 let response = QueryResponse::new(serde_json::json!({"id": 1}))
169 .with_execution_time(1000)
170 .with_metadata("cache_hit", serde_json::Value::Bool(false));
171
172 assert_eq!(response.execution_time_us, 1000);
173 assert!(!response.from_cache);
174 assert!(response.metadata.contains_key("cache_hit"));
175 }
176
177 #[test]
178 fn test_query_response_affected() {
179 let response = QueryResponse::with_affected(5);
180 assert_eq!(response.rows_affected, Some(5));
181 }
182
183 #[test]
184 fn test_query_response_from_cache() {
185 let response = QueryResponse::empty().from_cache();
186 assert!(response.from_cache);
187 }
188}
189