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:
21 Box<dyn FnOnce(QueryContext) -> BoxFuture<'a, MiddlewareResult<QueryResponse>> + Send + 'a>,
22}
23
24impl<'a> Next<'a> {
25 pub fn run(self, ctx: QueryContext) -> BoxFuture<'a, MiddlewareResult<QueryResponse>> {
27 (self.inner)(ctx)
28 }
29}
30
31#[derive(Debug, Clone)]
33pub struct QueryResponse {
34 pub data: serde_json::Value,
36 pub rows_affected: Option<u64>,
38 pub execution_time_us: u64,
40 pub from_cache: bool,
42 pub metadata: serde_json::Map<String, serde_json::Value>,
44}
45
46impl QueryResponse {
47 pub fn new(data: serde_json::Value) -> Self {
49 Self {
50 data,
51 rows_affected: None,
52 execution_time_us: 0,
53 from_cache: false,
54 metadata: serde_json::Map::new(),
55 }
56 }
57
58 pub fn empty() -> Self {
60 Self::new(serde_json::Value::Null)
61 }
62
63 pub fn with_affected(count: u64) -> Self {
65 Self {
66 data: serde_json::Value::Null,
67 rows_affected: Some(count),
68 execution_time_us: 0,
69 from_cache: false,
70 metadata: serde_json::Map::new(),
71 }
72 }
73
74 pub fn with_execution_time(mut self, us: u64) -> Self {
76 self.execution_time_us = us;
77 self
78 }
79
80 pub fn from_cache(mut self) -> Self {
82 self.from_cache = true;
83 self
84 }
85
86 pub fn with_metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
88 self.metadata.insert(key.into(), value);
89 self
90 }
91}
92
93pub trait Middleware: Send + Sync {
130 fn handle<'a>(
132 &'a self,
133 ctx: QueryContext,
134 next: Next<'a>,
135 ) -> BoxFuture<'a, MiddlewareResult<QueryResponse>>;
136
137 fn name(&self) -> &'static str {
139 std::any::type_name::<Self>()
140 }
141
142 fn enabled(&self) -> bool {
144 true
145 }
146}
147
148pub type SharedMiddleware = Arc<dyn Middleware>;
150
151pub trait IntoSharedMiddleware {
153 fn into_shared(self) -> SharedMiddleware;
155}
156
157impl<T: Middleware + 'static> IntoSharedMiddleware for T {
158 fn into_shared(self) -> SharedMiddleware {
159 Arc::new(self)
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn test_query_response_builder() {
169 let response = QueryResponse::new(serde_json::json!({"id": 1}))
170 .with_execution_time(1000)
171 .with_metadata("cache_hit", serde_json::Value::Bool(false));
172
173 assert_eq!(response.execution_time_us, 1000);
174 assert!(!response.from_cache);
175 assert!(response.metadata.contains_key("cache_hit"));
176 }
177
178 #[test]
179 fn test_query_response_affected() {
180 let response = QueryResponse::with_affected(5);
181 assert_eq!(response.rows_affected, Some(5));
182 }
183
184 #[test]
185 fn test_query_response_from_cache() {
186 let response = QueryResponse::empty().from_cache();
187 assert!(response.from_cache);
188 }
189}