1use crate::error::{ApiError, Result};
58use crate::json;
59use crate::request::Request;
60use crate::response::IntoResponse;
61use crate::stream::{StreamingBody, StreamingConfig};
62use crate::validation::Validatable;
63use bytes::Bytes;
64use http::{header, StatusCode};
65use rustapi_validate::v2::{AsyncValidate, ValidationContext};
66
67use rustapi_openapi::schema::{RustApiSchema, SchemaCtx, SchemaRef};
68use serde::de::DeserializeOwned;
69use serde::Serialize;
70use std::collections::BTreeMap;
71use std::future::Future;
72use std::ops::{Deref, DerefMut};
73use std::str::FromStr;
74
75pub trait FromRequestParts: Sized {
100 fn from_request_parts(req: &Request) -> Result<Self>;
102}
103
104pub trait FromRequest: Sized {
134 fn from_request(req: &mut Request) -> impl Future<Output = Result<Self>> + Send;
136}
137
138impl<T: FromRequestParts> FromRequest for T {
140 async fn from_request(req: &mut Request) -> Result<Self> {
141 T::from_request_parts(req)
142 }
143}
144
145#[derive(Debug, Clone, Copy, Default)]
164pub struct Json<T>(pub T);
165
166impl<T: DeserializeOwned + Send> FromRequest for Json<T> {
167 async fn from_request(req: &mut Request) -> Result<Self> {
168 req.load_body().await?;
169 let body = req
170 .take_body()
171 .ok_or_else(|| ApiError::internal("Body already consumed"))?;
172
173 let value: T = json::from_slice(&body)?;
175 Ok(Json(value))
176 }
177}
178
179impl<T> Deref for Json<T> {
180 type Target = T;
181
182 fn deref(&self) -> &Self::Target {
183 &self.0
184 }
185}
186
187impl<T> DerefMut for Json<T> {
188 fn deref_mut(&mut self) -> &mut Self::Target {
189 &mut self.0
190 }
191}
192
193impl<T> From<T> for Json<T> {
194 fn from(value: T) -> Self {
195 Json(value)
196 }
197}
198
199const JSON_RESPONSE_INITIAL_CAPACITY: usize = 256;
202
203impl<T: Serialize> IntoResponse for Json<T> {
205 fn into_response(self) -> crate::response::Response {
206 match json::to_vec_with_capacity(&self.0, JSON_RESPONSE_INITIAL_CAPACITY) {
208 Ok(body) => http::Response::builder()
209 .status(StatusCode::OK)
210 .header(header::CONTENT_TYPE, "application/json")
211 .body(crate::response::Body::from(body))
212 .unwrap(),
213 Err(err) => {
214 ApiError::internal(format!("Failed to serialize response: {}", err)).into_response()
215 }
216 }
217 }
218}
219
220#[derive(Debug, Clone, Copy, Default)]
246pub struct ValidatedJson<T>(pub T);
247
248impl<T> ValidatedJson<T> {
249 pub fn new(value: T) -> Self {
251 Self(value)
252 }
253
254 pub fn into_inner(self) -> T {
256 self.0
257 }
258}
259
260impl<T: DeserializeOwned + Validatable + Send> FromRequest for ValidatedJson<T> {
261 async fn from_request(req: &mut Request) -> Result<Self> {
262 req.load_body().await?;
263 let body = req
265 .take_body()
266 .ok_or_else(|| ApiError::internal("Body already consumed"))?;
267
268 let value: T = json::from_slice(&body)?;
269
270 value.do_validate()?;
272
273 Ok(ValidatedJson(value))
274 }
275}
276
277impl<T> Deref for ValidatedJson<T> {
278 type Target = T;
279
280 fn deref(&self) -> &Self::Target {
281 &self.0
282 }
283}
284
285impl<T> DerefMut for ValidatedJson<T> {
286 fn deref_mut(&mut self) -> &mut Self::Target {
287 &mut self.0
288 }
289}
290
291impl<T> From<T> for ValidatedJson<T> {
292 fn from(value: T) -> Self {
293 ValidatedJson(value)
294 }
295}
296
297impl<T: Serialize> IntoResponse for ValidatedJson<T> {
298 fn into_response(self) -> crate::response::Response {
299 Json(self.0).into_response()
300 }
301}
302
303#[derive(Debug, Clone, Copy, Default)]
330pub struct AsyncValidatedJson<T>(pub T);
331
332impl<T> AsyncValidatedJson<T> {
333 pub fn new(value: T) -> Self {
335 Self(value)
336 }
337
338 pub fn into_inner(self) -> T {
340 self.0
341 }
342}
343
344impl<T> Deref for AsyncValidatedJson<T> {
345 type Target = T;
346
347 fn deref(&self) -> &Self::Target {
348 &self.0
349 }
350}
351
352impl<T> DerefMut for AsyncValidatedJson<T> {
353 fn deref_mut(&mut self) -> &mut Self::Target {
354 &mut self.0
355 }
356}
357
358impl<T> From<T> for AsyncValidatedJson<T> {
359 fn from(value: T) -> Self {
360 AsyncValidatedJson(value)
361 }
362}
363
364impl<T: Serialize> IntoResponse for AsyncValidatedJson<T> {
365 fn into_response(self) -> crate::response::Response {
366 Json(self.0).into_response()
367 }
368}
369
370impl<T: DeserializeOwned + AsyncValidate + Send + Sync> FromRequest for AsyncValidatedJson<T> {
371 async fn from_request(req: &mut Request) -> Result<Self> {
372 req.load_body().await?;
373
374 let body = req
375 .take_body()
376 .ok_or_else(|| ApiError::internal("Body already consumed"))?;
377
378 let value: T = json::from_slice(&body)?;
379
380 let ctx = if let Some(ctx) = req.state().get::<ValidationContext>() {
383 ctx.clone()
384 } else {
385 ValidationContext::default()
386 };
387
388 if let Err(errors) = value.validate_full(&ctx).await {
390 let field_errors: Vec<crate::error::FieldError> = errors
392 .fields
393 .iter()
394 .flat_map(|(field, errs)| {
395 let field_name = field.to_string();
396 errs.iter().map(move |e| crate::error::FieldError {
397 field: field_name.clone(),
398 code: e.code.to_string(),
399 message: e.message.clone(),
400 })
401 })
402 .collect();
403
404 return Err(ApiError::validation(field_errors));
405 }
406
407 Ok(AsyncValidatedJson(value))
408 }
409}
410
411#[derive(Debug, Clone)]
429pub struct Query<T>(pub T);
430
431impl<T: DeserializeOwned> FromRequestParts for Query<T> {
432 fn from_request_parts(req: &Request) -> Result<Self> {
433 let query = req.query_string().unwrap_or("");
434 let value: T = serde_urlencoded::from_str(query)
435 .map_err(|e| ApiError::bad_request(format!("Invalid query string: {}", e)))?;
436 Ok(Query(value))
437 }
438}
439
440impl<T> Deref for Query<T> {
441 type Target = T;
442
443 fn deref(&self) -> &Self::Target {
444 &self.0
445 }
446}
447
448#[derive(Debug, Clone)]
470pub struct Path<T>(pub T);
471
472impl<T: FromStr> FromRequestParts for Path<T>
473where
474 T::Err: std::fmt::Display,
475{
476 fn from_request_parts(req: &Request) -> Result<Self> {
477 let params = req.path_params();
478
479 if let Some((_, value)) = params.iter().next() {
481 let parsed = value
482 .parse::<T>()
483 .map_err(|e| ApiError::bad_request(format!("Invalid path parameter: {}", e)))?;
484 return Ok(Path(parsed));
485 }
486
487 Err(ApiError::internal("Missing path parameter"))
488 }
489}
490
491impl<T> Deref for Path<T> {
492 type Target = T;
493
494 fn deref(&self) -> &Self::Target {
495 &self.0
496 }
497}
498
499#[derive(Debug, Clone)]
519pub struct Typed<T>(pub T);
520
521impl<T: DeserializeOwned + Send> FromRequestParts for Typed<T> {
522 fn from_request_parts(req: &Request) -> Result<Self> {
523 let params = req.path_params();
524 let mut map = serde_json::Map::new();
525 for (k, v) in params.iter() {
526 map.insert(k.to_string(), serde_json::Value::String(v.to_string()));
527 }
528 let value = serde_json::Value::Object(map);
529 let parsed: T = serde_json::from_value(value)
530 .map_err(|e| ApiError::bad_request(format!("Invalid path parameters: {}", e)))?;
531 Ok(Typed(parsed))
532 }
533}
534
535impl<T> Deref for Typed<T> {
536 type Target = T;
537
538 fn deref(&self) -> &Self::Target {
539 &self.0
540 }
541}
542
543#[derive(Debug, Clone)]
560pub struct State<T>(pub T);
561
562impl<T: Clone + Send + Sync + 'static> FromRequestParts for State<T> {
563 fn from_request_parts(req: &Request) -> Result<Self> {
564 req.state().get::<T>().cloned().map(State).ok_or_else(|| {
565 ApiError::internal(format!(
566 "State of type `{}` not found. Did you forget to call .state()?",
567 std::any::type_name::<T>()
568 ))
569 })
570 }
571}
572
573impl<T> Deref for State<T> {
574 type Target = T;
575
576 fn deref(&self) -> &Self::Target {
577 &self.0
578 }
579}
580
581#[derive(Debug, Clone)]
583pub struct Body(pub Bytes);
584
585impl FromRequest for Body {
586 async fn from_request(req: &mut Request) -> Result<Self> {
587 req.load_body().await?;
588 let body = req
589 .take_body()
590 .ok_or_else(|| ApiError::internal("Body already consumed"))?;
591 Ok(Body(body))
592 }
593}
594
595impl Deref for Body {
596 type Target = Bytes;
597
598 fn deref(&self) -> &Self::Target {
599 &self.0
600 }
601}
602
603pub struct BodyStream(pub StreamingBody);
605
606impl FromRequest for BodyStream {
607 async fn from_request(req: &mut Request) -> Result<Self> {
608 let config = StreamingConfig::default();
609
610 if let Some(stream) = req.take_stream() {
611 Ok(BodyStream(StreamingBody::new(stream, config.max_body_size)))
612 } else if let Some(bytes) = req.take_body() {
613 let stream = futures_util::stream::once(async move { Ok(bytes) });
615 Ok(BodyStream(StreamingBody::from_stream(
616 stream,
617 config.max_body_size,
618 )))
619 } else {
620 Err(ApiError::internal("Body already consumed"))
621 }
622 }
623}
624
625impl Deref for BodyStream {
626 type Target = StreamingBody;
627
628 fn deref(&self) -> &Self::Target {
629 &self.0
630 }
631}
632
633impl DerefMut for BodyStream {
634 fn deref_mut(&mut self) -> &mut Self::Target {
635 &mut self.0
636 }
637}
638
639impl futures_util::Stream for BodyStream {
641 type Item = Result<Bytes, ApiError>;
642
643 fn poll_next(
644 mut self: std::pin::Pin<&mut Self>,
645 cx: &mut std::task::Context<'_>,
646 ) -> std::task::Poll<Option<Self::Item>> {
647 std::pin::Pin::new(&mut self.0).poll_next(cx)
648 }
649}
650
651impl<T: FromRequestParts> FromRequestParts for Option<T> {
655 fn from_request_parts(req: &Request) -> Result<Self> {
656 Ok(T::from_request_parts(req).ok())
657 }
658}
659
660#[derive(Debug, Clone)]
678pub struct Headers(pub http::HeaderMap);
679
680impl Headers {
681 pub fn get(&self, name: &str) -> Option<&http::HeaderValue> {
683 self.0.get(name)
684 }
685
686 pub fn contains(&self, name: &str) -> bool {
688 self.0.contains_key(name)
689 }
690
691 pub fn len(&self) -> usize {
693 self.0.len()
694 }
695
696 pub fn is_empty(&self) -> bool {
698 self.0.is_empty()
699 }
700
701 pub fn iter(&self) -> http::header::Iter<'_, http::HeaderValue> {
703 self.0.iter()
704 }
705}
706
707impl FromRequestParts for Headers {
708 fn from_request_parts(req: &Request) -> Result<Self> {
709 Ok(Headers(req.headers().clone()))
710 }
711}
712
713impl Deref for Headers {
714 type Target = http::HeaderMap;
715
716 fn deref(&self) -> &Self::Target {
717 &self.0
718 }
719}
720
721#[derive(Debug, Clone)]
740pub struct HeaderValue(pub String, pub &'static str);
741
742impl HeaderValue {
743 pub fn new(name: &'static str, value: String) -> Self {
745 Self(value, name)
746 }
747
748 pub fn value(&self) -> &str {
750 &self.0
751 }
752
753 pub fn name(&self) -> &'static str {
755 self.1
756 }
757
758 pub fn extract(req: &Request, name: &'static str) -> Result<Self> {
760 req.headers()
761 .get(name)
762 .and_then(|v| v.to_str().ok())
763 .map(|s| HeaderValue(s.to_string(), name))
764 .ok_or_else(|| ApiError::bad_request(format!("Missing required header: {}", name)))
765 }
766}
767
768impl Deref for HeaderValue {
769 type Target = String;
770
771 fn deref(&self) -> &Self::Target {
772 &self.0
773 }
774}
775
776#[derive(Debug, Clone)]
794pub struct Extension<T>(pub T);
795
796impl<T: Clone + Send + Sync + 'static> FromRequestParts for Extension<T> {
797 fn from_request_parts(req: &Request) -> Result<Self> {
798 req.extensions()
799 .get::<T>()
800 .cloned()
801 .map(Extension)
802 .ok_or_else(|| {
803 ApiError::internal(format!(
804 "Extension of type `{}` not found. Did middleware insert it?",
805 std::any::type_name::<T>()
806 ))
807 })
808 }
809}
810
811impl<T> Deref for Extension<T> {
812 type Target = T;
813
814 fn deref(&self) -> &Self::Target {
815 &self.0
816 }
817}
818
819impl<T> DerefMut for Extension<T> {
820 fn deref_mut(&mut self) -> &mut Self::Target {
821 &mut self.0
822 }
823}
824
825#[derive(Debug, Clone)]
840pub struct ClientIp(pub std::net::IpAddr);
841
842impl ClientIp {
843 pub fn extract_with_config(req: &Request, trust_proxy: bool) -> Result<Self> {
845 if trust_proxy {
846 if let Some(forwarded) = req.headers().get("x-forwarded-for") {
848 if let Ok(forwarded_str) = forwarded.to_str() {
849 if let Some(first_ip) = forwarded_str.split(',').next() {
851 if let Ok(ip) = first_ip.trim().parse() {
852 return Ok(ClientIp(ip));
853 }
854 }
855 }
856 }
857 }
858
859 if let Some(addr) = req.extensions().get::<std::net::SocketAddr>() {
861 return Ok(ClientIp(addr.ip()));
862 }
863
864 Ok(ClientIp(std::net::IpAddr::V4(std::net::Ipv4Addr::new(
866 127, 0, 0, 1,
867 ))))
868 }
869}
870
871impl FromRequestParts for ClientIp {
872 fn from_request_parts(req: &Request) -> Result<Self> {
873 Self::extract_with_config(req, true)
875 }
876}
877
878#[cfg(feature = "cookies")]
896#[derive(Debug, Clone)]
897pub struct Cookies(pub cookie::CookieJar);
898
899#[cfg(feature = "cookies")]
900impl Cookies {
901 pub fn get(&self, name: &str) -> Option<&cookie::Cookie<'static>> {
903 self.0.get(name)
904 }
905
906 pub fn iter(&self) -> impl Iterator<Item = &cookie::Cookie<'static>> {
908 self.0.iter()
909 }
910
911 pub fn contains(&self, name: &str) -> bool {
913 self.0.get(name).is_some()
914 }
915}
916
917#[cfg(feature = "cookies")]
918impl FromRequestParts for Cookies {
919 fn from_request_parts(req: &Request) -> Result<Self> {
920 let mut jar = cookie::CookieJar::new();
921
922 if let Some(cookie_header) = req.headers().get(header::COOKIE) {
923 if let Ok(cookie_str) = cookie_header.to_str() {
924 for cookie_part in cookie_str.split(';') {
926 let trimmed = cookie_part.trim();
927 if !trimmed.is_empty() {
928 if let Ok(cookie) = cookie::Cookie::parse(trimmed.to_string()) {
929 jar.add_original(cookie.into_owned());
930 }
931 }
932 }
933 }
934 }
935
936 Ok(Cookies(jar))
937 }
938}
939
940#[cfg(feature = "cookies")]
941impl Deref for Cookies {
942 type Target = cookie::CookieJar;
943
944 fn deref(&self) -> &Self::Target {
945 &self.0
946 }
947}
948
949macro_rules! impl_from_request_parts_for_primitives {
951 ($($ty:ty),*) => {
952 $(
953 impl FromRequestParts for $ty {
954 fn from_request_parts(req: &Request) -> Result<Self> {
955 let Path(value) = Path::<$ty>::from_request_parts(req)?;
956 Ok(value)
957 }
958 }
959 )*
960 };
961}
962
963impl_from_request_parts_for_primitives!(
964 i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64, bool, String
965);
966
967use rustapi_openapi::{
970 MediaType, Operation, OperationModifier, Parameter, RequestBody, ResponseModifier, ResponseSpec,
971};
972
973impl<T: RustApiSchema> OperationModifier for ValidatedJson<T> {
975 fn update_operation(op: &mut Operation) {
976 let mut ctx = SchemaCtx::new();
977 let schema_ref = T::schema(&mut ctx);
978
979 let mut content = BTreeMap::new();
980 content.insert(
981 "application/json".to_string(),
982 MediaType {
983 schema: Some(schema_ref),
984 example: None,
985 },
986 );
987
988 op.request_body = Some(RequestBody {
989 description: None,
990 required: Some(true),
991 content,
992 });
993
994 let mut responses_content = BTreeMap::new();
996 responses_content.insert(
997 "application/json".to_string(),
998 MediaType {
999 schema: Some(SchemaRef::Ref {
1000 reference: "#/components/schemas/ValidationErrorSchema".to_string(),
1001 }),
1002 example: None,
1003 },
1004 );
1005
1006 op.responses.insert(
1007 "422".to_string(),
1008 ResponseSpec {
1009 description: "Validation Error".to_string(),
1010 content: responses_content,
1011 headers: BTreeMap::new(),
1012 },
1013 );
1014 }
1015
1016 fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
1017 spec.register_in_place::<T>();
1018 spec.register_in_place::<rustapi_openapi::ValidationErrorSchema>();
1019 spec.register_in_place::<rustapi_openapi::ValidationErrorBodySchema>();
1020 spec.register_in_place::<rustapi_openapi::FieldErrorSchema>();
1021 }
1022}
1023
1024impl<T: RustApiSchema> OperationModifier for AsyncValidatedJson<T> {
1026 fn update_operation(op: &mut Operation) {
1027 let mut ctx = SchemaCtx::new();
1028 let schema_ref = T::schema(&mut ctx);
1029
1030 let mut content = BTreeMap::new();
1031 content.insert(
1032 "application/json".to_string(),
1033 MediaType {
1034 schema: Some(schema_ref),
1035 example: None,
1036 },
1037 );
1038
1039 op.request_body = Some(RequestBody {
1040 description: None,
1041 required: Some(true),
1042 content,
1043 });
1044
1045 let mut responses_content = BTreeMap::new();
1047 responses_content.insert(
1048 "application/json".to_string(),
1049 MediaType {
1050 schema: Some(SchemaRef::Ref {
1051 reference: "#/components/schemas/ValidationErrorSchema".to_string(),
1052 }),
1053 example: None,
1054 },
1055 );
1056
1057 op.responses.insert(
1058 "422".to_string(),
1059 ResponseSpec {
1060 description: "Validation Error".to_string(),
1061 content: responses_content,
1062 headers: BTreeMap::new(),
1063 },
1064 );
1065 }
1066
1067 fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
1068 spec.register_in_place::<T>();
1069 spec.register_in_place::<rustapi_openapi::ValidationErrorSchema>();
1070 spec.register_in_place::<rustapi_openapi::ValidationErrorBodySchema>();
1071 spec.register_in_place::<rustapi_openapi::FieldErrorSchema>();
1072 }
1073}
1074
1075impl<T: RustApiSchema> OperationModifier for Json<T> {
1077 fn update_operation(op: &mut Operation) {
1078 let mut ctx = SchemaCtx::new();
1079 let schema_ref = T::schema(&mut ctx);
1080
1081 let mut content = BTreeMap::new();
1082 content.insert(
1083 "application/json".to_string(),
1084 MediaType {
1085 schema: Some(schema_ref),
1086 example: None,
1087 },
1088 );
1089
1090 op.request_body = Some(RequestBody {
1091 description: None,
1092 required: Some(true),
1093 content,
1094 });
1095 }
1096
1097 fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
1098 spec.register_in_place::<T>();
1099 }
1100}
1101
1102impl<T> OperationModifier for Path<T> {
1104 fn update_operation(_op: &mut Operation) {}
1105}
1106
1107impl<T> OperationModifier for Typed<T> {
1109 fn update_operation(_op: &mut Operation) {}
1110}
1111
1112impl<T: RustApiSchema> OperationModifier for Query<T> {
1114 fn update_operation(op: &mut Operation) {
1115 let mut ctx = SchemaCtx::new();
1116 if let Some(fields) = T::field_schemas(&mut ctx) {
1117 let new_params: Vec<Parameter> = fields
1118 .into_iter()
1119 .map(|(name, schema)| {
1120 Parameter {
1121 name,
1122 location: "query".to_string(),
1123 required: false, deprecated: None,
1125 description: None,
1126 schema: Some(schema),
1127 }
1128 })
1129 .collect();
1130
1131 op.parameters.extend(new_params);
1132 }
1133 }
1134
1135 fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
1136 spec.register_in_place::<T>();
1137 }
1138}
1139
1140impl<T> OperationModifier for State<T> {
1142 fn update_operation(_op: &mut Operation) {}
1143}
1144
1145impl OperationModifier for Body {
1147 fn update_operation(op: &mut Operation) {
1148 let mut content = BTreeMap::new();
1149 content.insert(
1150 "application/octet-stream".to_string(),
1151 MediaType {
1152 schema: Some(SchemaRef::Inline(
1153 serde_json::json!({ "type": "string", "format": "binary" }),
1154 )),
1155 example: None,
1156 },
1157 );
1158
1159 op.request_body = Some(RequestBody {
1160 description: None,
1161 required: Some(true),
1162 content,
1163 });
1164 }
1165}
1166
1167impl OperationModifier for BodyStream {
1169 fn update_operation(op: &mut Operation) {
1170 let mut content = BTreeMap::new();
1171 content.insert(
1172 "application/octet-stream".to_string(),
1173 MediaType {
1174 schema: Some(SchemaRef::Inline(
1175 serde_json::json!({ "type": "string", "format": "binary" }),
1176 )),
1177 example: None,
1178 },
1179 );
1180
1181 op.request_body = Some(RequestBody {
1182 description: None,
1183 required: Some(true),
1184 content,
1185 });
1186 }
1187}
1188
1189impl<T: RustApiSchema> ResponseModifier for Json<T> {
1193 fn update_response(op: &mut Operation) {
1194 let mut ctx = SchemaCtx::new();
1195 let schema_ref = T::schema(&mut ctx);
1196
1197 let mut content = BTreeMap::new();
1198 content.insert(
1199 "application/json".to_string(),
1200 MediaType {
1201 schema: Some(schema_ref),
1202 example: None,
1203 },
1204 );
1205
1206 op.responses.insert(
1207 "200".to_string(),
1208 ResponseSpec {
1209 description: "Successful response".to_string(),
1210 content,
1211 headers: BTreeMap::new(),
1212 },
1213 );
1214 }
1215
1216 fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
1217 spec.register_in_place::<T>();
1218 }
1219}
1220
1221impl<T: RustApiSchema> RustApiSchema for Json<T> {
1224 fn schema(ctx: &mut SchemaCtx) -> SchemaRef {
1225 T::schema(ctx)
1226 }
1227}
1228
1229impl<T: RustApiSchema> RustApiSchema for ValidatedJson<T> {
1230 fn schema(ctx: &mut SchemaCtx) -> SchemaRef {
1231 T::schema(ctx)
1232 }
1233}
1234
1235impl<T: RustApiSchema> RustApiSchema for AsyncValidatedJson<T> {
1236 fn schema(ctx: &mut SchemaCtx) -> SchemaRef {
1237 T::schema(ctx)
1238 }
1239}
1240
1241impl<T: RustApiSchema> RustApiSchema for Query<T> {
1242 fn schema(ctx: &mut SchemaCtx) -> SchemaRef {
1243 T::schema(ctx)
1244 }
1245 fn field_schemas(ctx: &mut SchemaCtx) -> Option<BTreeMap<String, SchemaRef>> {
1246 T::field_schemas(ctx)
1247 }
1248}
1249
1250const DEFAULT_PAGE: u64 = 1;
1254const DEFAULT_PER_PAGE: u64 = 20;
1256const MAX_PER_PAGE: u64 = 100;
1258
1259#[derive(Debug, Clone, Copy)]
1276pub struct Paginate {
1277 pub page: u64,
1279 pub per_page: u64,
1281}
1282
1283impl Paginate {
1284 pub fn new(page: u64, per_page: u64) -> Self {
1286 Self {
1287 page: page.max(1),
1288 per_page: per_page.clamp(1, MAX_PER_PAGE),
1289 }
1290 }
1291
1292 pub fn offset(&self) -> u64 {
1294 (self.page - 1) * self.per_page
1295 }
1296
1297 pub fn limit(&self) -> u64 {
1299 self.per_page
1300 }
1301
1302 pub fn paginate<T>(self, items: Vec<T>, total: u64) -> crate::hateoas::Paginated<T> {
1304 crate::hateoas::Paginated {
1305 items,
1306 page: self.page,
1307 per_page: self.per_page,
1308 total,
1309 }
1310 }
1311}
1312
1313impl Default for Paginate {
1314 fn default() -> Self {
1315 Self {
1316 page: DEFAULT_PAGE,
1317 per_page: DEFAULT_PER_PAGE,
1318 }
1319 }
1320}
1321
1322impl FromRequestParts for Paginate {
1323 fn from_request_parts(req: &Request) -> Result<Self> {
1324 let query = req.query_string().unwrap_or("");
1325
1326 #[derive(serde::Deserialize)]
1327 struct PaginateQuery {
1328 page: Option<u64>,
1329 per_page: Option<u64>,
1330 }
1331
1332 let params: PaginateQuery = serde_urlencoded::from_str(query).unwrap_or(PaginateQuery {
1333 page: None,
1334 per_page: None,
1335 });
1336
1337 Ok(Paginate::new(
1338 params.page.unwrap_or(DEFAULT_PAGE),
1339 params.per_page.unwrap_or(DEFAULT_PER_PAGE),
1340 ))
1341 }
1342}
1343
1344#[derive(Debug, Clone)]
1367pub struct CursorPaginate {
1368 pub cursor: Option<String>,
1370 pub per_page: u64,
1372}
1373
1374impl CursorPaginate {
1375 pub fn new(cursor: Option<String>, per_page: u64) -> Self {
1377 Self {
1378 cursor,
1379 per_page: per_page.clamp(1, MAX_PER_PAGE),
1380 }
1381 }
1382
1383 pub fn after(&self) -> Option<&str> {
1385 self.cursor.as_deref()
1386 }
1387
1388 pub fn limit(&self) -> u64 {
1390 self.per_page
1391 }
1392
1393 pub fn is_first_page(&self) -> bool {
1395 self.cursor.is_none()
1396 }
1397}
1398
1399impl Default for CursorPaginate {
1400 fn default() -> Self {
1401 Self {
1402 cursor: None,
1403 per_page: DEFAULT_PER_PAGE,
1404 }
1405 }
1406}
1407
1408impl FromRequestParts for CursorPaginate {
1409 fn from_request_parts(req: &Request) -> Result<Self> {
1410 let query = req.query_string().unwrap_or("");
1411
1412 #[derive(serde::Deserialize)]
1413 struct CursorQuery {
1414 cursor: Option<String>,
1415 limit: Option<u64>,
1416 }
1417
1418 let params: CursorQuery = serde_urlencoded::from_str(query).unwrap_or(CursorQuery {
1419 cursor: None,
1420 limit: None,
1421 });
1422
1423 Ok(CursorPaginate::new(
1424 params.cursor,
1425 params.limit.unwrap_or(DEFAULT_PER_PAGE),
1426 ))
1427 }
1428}
1429
1430#[cfg(test)]
1431mod tests {
1432 use super::*;
1433 use crate::path_params::PathParams;
1434 use bytes::Bytes;
1435 use http::{Extensions, Method};
1436 use proptest::prelude::*;
1437 use proptest::test_runner::TestCaseError;
1438 use std::sync::Arc;
1439
1440 fn create_test_request_with_headers(
1442 method: Method,
1443 path: &str,
1444 headers: Vec<(&str, &str)>,
1445 ) -> Request {
1446 let uri: http::Uri = path.parse().unwrap();
1447 let mut builder = http::Request::builder().method(method).uri(uri);
1448
1449 for (name, value) in headers {
1450 builder = builder.header(name, value);
1451 }
1452
1453 let req = builder.body(()).unwrap();
1454 let (parts, _) = req.into_parts();
1455
1456 Request::new(
1457 parts,
1458 crate::request::BodyVariant::Buffered(Bytes::new()),
1459 Arc::new(Extensions::new()),
1460 PathParams::new(),
1461 )
1462 }
1463
1464 fn create_test_request_with_extensions<T: Clone + Send + Sync + 'static>(
1466 method: Method,
1467 path: &str,
1468 extension: T,
1469 ) -> Request {
1470 let uri: http::Uri = path.parse().unwrap();
1471 let builder = http::Request::builder().method(method).uri(uri);
1472
1473 let req = builder.body(()).unwrap();
1474 let (mut parts, _) = req.into_parts();
1475 parts.extensions.insert(extension);
1476
1477 Request::new(
1478 parts,
1479 crate::request::BodyVariant::Buffered(Bytes::new()),
1480 Arc::new(Extensions::new()),
1481 PathParams::new(),
1482 )
1483 }
1484
1485 proptest! {
1492 #![proptest_config(ProptestConfig::with_cases(100))]
1493
1494 #[test]
1495 fn prop_headers_extractor_completeness(
1496 headers in prop::collection::vec(
1499 (
1500 "[a-z][a-z0-9-]{0,20}", "[a-zA-Z0-9 ]{1,50}" ),
1503 0..10
1504 )
1505 ) {
1506 let result: Result<(), TestCaseError> = (|| {
1507 let header_tuples: Vec<(&str, &str)> = headers
1509 .iter()
1510 .map(|(k, v)| (k.as_str(), v.as_str()))
1511 .collect();
1512
1513 let request = create_test_request_with_headers(
1515 Method::GET,
1516 "/test",
1517 header_tuples.clone(),
1518 );
1519
1520 let extracted = Headers::from_request_parts(&request)
1522 .map_err(|e| TestCaseError::fail(format!("Failed to extract headers: {}", e)))?;
1523
1524 for (name, value) in &headers {
1527 let all_values: Vec<_> = extracted.get_all(name.as_str()).iter().collect();
1529 prop_assert!(
1530 !all_values.is_empty(),
1531 "Header '{}' not found",
1532 name
1533 );
1534
1535 let value_found = all_values.iter().any(|v| {
1537 v.to_str().map(|s| s == value.as_str()).unwrap_or(false)
1538 });
1539
1540 prop_assert!(
1541 value_found,
1542 "Header '{}' value '{}' not found in extracted values",
1543 name,
1544 value
1545 );
1546 }
1547
1548 Ok(())
1549 })();
1550 result?;
1551 }
1552 }
1553
1554 proptest! {
1561 #![proptest_config(ProptestConfig::with_cases(100))]
1562
1563 #[test]
1564 fn prop_header_value_extractor_correctness(
1565 header_name in "[a-z][a-z0-9-]{0,20}",
1566 header_value in "[a-zA-Z0-9 ]{1,50}",
1567 has_header in prop::bool::ANY,
1568 ) {
1569 let result: Result<(), TestCaseError> = (|| {
1570 let headers = if has_header {
1571 vec![(header_name.as_str(), header_value.as_str())]
1572 } else {
1573 vec![]
1574 };
1575
1576 let _request = create_test_request_with_headers(Method::GET, "/test", headers);
1577
1578 let test_header = "x-test-header";
1581 let request_with_known_header = if has_header {
1582 create_test_request_with_headers(
1583 Method::GET,
1584 "/test",
1585 vec![(test_header, header_value.as_str())],
1586 )
1587 } else {
1588 create_test_request_with_headers(Method::GET, "/test", vec![])
1589 };
1590
1591 let result = HeaderValue::extract(&request_with_known_header, test_header);
1592
1593 if has_header {
1594 let extracted = result
1595 .map_err(|e| TestCaseError::fail(format!("Expected header to be found: {}", e)))?;
1596 prop_assert_eq!(
1597 extracted.value(),
1598 header_value.as_str(),
1599 "Header value mismatch"
1600 );
1601 } else {
1602 prop_assert!(
1603 result.is_err(),
1604 "Expected error when header is missing"
1605 );
1606 }
1607
1608 Ok(())
1609 })();
1610 result?;
1611 }
1612 }
1613
1614 proptest! {
1621 #![proptest_config(ProptestConfig::with_cases(100))]
1622
1623 #[test]
1624 fn prop_client_ip_extractor_with_forwarding(
1625 forwarded_ip in (0u8..=255, 0u8..=255, 0u8..=255, 0u8..=255)
1627 .prop_map(|(a, b, c, d)| format!("{}.{}.{}.{}", a, b, c, d)),
1628 socket_ip in (0u8..=255, 0u8..=255, 0u8..=255, 0u8..=255)
1629 .prop_map(|(a, b, c, d)| std::net::IpAddr::V4(std::net::Ipv4Addr::new(a, b, c, d))),
1630 has_forwarded_header in prop::bool::ANY,
1631 trust_proxy in prop::bool::ANY,
1632 ) {
1633 let result: Result<(), TestCaseError> = (|| {
1634 let headers = if has_forwarded_header {
1635 vec![("x-forwarded-for", forwarded_ip.as_str())]
1636 } else {
1637 vec![]
1638 };
1639
1640 let uri: http::Uri = "/test".parse().unwrap();
1642 let mut builder = http::Request::builder().method(Method::GET).uri(uri);
1643 for (name, value) in &headers {
1644 builder = builder.header(*name, *value);
1645 }
1646 let req = builder.body(()).unwrap();
1647 let (mut parts, _) = req.into_parts();
1648
1649 let socket_addr = std::net::SocketAddr::new(socket_ip, 8080);
1651 parts.extensions.insert(socket_addr);
1652
1653 let request = Request::new(
1654 parts,
1655 crate::request::BodyVariant::Buffered(Bytes::new()),
1656 Arc::new(Extensions::new()),
1657 PathParams::new(),
1658 );
1659
1660 let extracted = ClientIp::extract_with_config(&request, trust_proxy)
1661 .map_err(|e| TestCaseError::fail(format!("Failed to extract ClientIp: {}", e)))?;
1662
1663 if trust_proxy && has_forwarded_header {
1664 let expected_ip: std::net::IpAddr = forwarded_ip.parse()
1666 .map_err(|e| TestCaseError::fail(format!("Invalid IP: {}", e)))?;
1667 prop_assert_eq!(
1668 extracted.0,
1669 expected_ip,
1670 "Should use X-Forwarded-For IP when trust_proxy is enabled"
1671 );
1672 } else {
1673 prop_assert_eq!(
1675 extracted.0,
1676 socket_ip,
1677 "Should use socket IP when trust_proxy is disabled or no X-Forwarded-For"
1678 );
1679 }
1680
1681 Ok(())
1682 })();
1683 result?;
1684 }
1685 }
1686
1687 proptest! {
1694 #![proptest_config(ProptestConfig::with_cases(100))]
1695
1696 #[test]
1697 fn prop_extension_extractor_retrieval(
1698 value in any::<i64>(),
1699 has_extension in prop::bool::ANY,
1700 ) {
1701 let result: Result<(), TestCaseError> = (|| {
1702 #[derive(Clone, Debug, PartialEq)]
1704 struct TestExtension(i64);
1705
1706 let uri: http::Uri = "/test".parse().unwrap();
1707 let builder = http::Request::builder().method(Method::GET).uri(uri);
1708 let req = builder.body(()).unwrap();
1709 let (mut parts, _) = req.into_parts();
1710
1711 if has_extension {
1712 parts.extensions.insert(TestExtension(value));
1713 }
1714
1715 let request = Request::new(
1716 parts,
1717 crate::request::BodyVariant::Buffered(Bytes::new()),
1718 Arc::new(Extensions::new()),
1719 PathParams::new(),
1720 );
1721
1722 let result = Extension::<TestExtension>::from_request_parts(&request);
1723
1724 if has_extension {
1725 let extracted = result
1726 .map_err(|e| TestCaseError::fail(format!("Expected extension to be found: {}", e)))?;
1727 prop_assert_eq!(
1728 extracted.0,
1729 TestExtension(value),
1730 "Extension value mismatch"
1731 );
1732 } else {
1733 prop_assert!(
1734 result.is_err(),
1735 "Expected error when extension is missing"
1736 );
1737 }
1738
1739 Ok(())
1740 })();
1741 result?;
1742 }
1743 }
1744
1745 #[test]
1748 fn test_headers_extractor_basic() {
1749 let request = create_test_request_with_headers(
1750 Method::GET,
1751 "/test",
1752 vec![
1753 ("content-type", "application/json"),
1754 ("accept", "text/html"),
1755 ],
1756 );
1757
1758 let headers = Headers::from_request_parts(&request).unwrap();
1759
1760 assert!(headers.contains("content-type"));
1761 assert!(headers.contains("accept"));
1762 assert!(!headers.contains("x-custom"));
1763 assert_eq!(headers.len(), 2);
1764 }
1765
1766 #[test]
1767 fn test_header_value_extractor_present() {
1768 let request = create_test_request_with_headers(
1769 Method::GET,
1770 "/test",
1771 vec![("authorization", "Bearer token123")],
1772 );
1773
1774 let result = HeaderValue::extract(&request, "authorization");
1775 assert!(result.is_ok());
1776 assert_eq!(result.unwrap().value(), "Bearer token123");
1777 }
1778
1779 #[test]
1780 fn test_header_value_extractor_missing() {
1781 let request = create_test_request_with_headers(Method::GET, "/test", vec![]);
1782
1783 let result = HeaderValue::extract(&request, "authorization");
1784 assert!(result.is_err());
1785 }
1786
1787 #[test]
1788 fn test_client_ip_from_forwarded_header() {
1789 let request = create_test_request_with_headers(
1790 Method::GET,
1791 "/test",
1792 vec![("x-forwarded-for", "192.168.1.100, 10.0.0.1")],
1793 );
1794
1795 let ip = ClientIp::extract_with_config(&request, true).unwrap();
1796 assert_eq!(ip.0, "192.168.1.100".parse::<std::net::IpAddr>().unwrap());
1797 }
1798
1799 #[test]
1800 fn test_client_ip_ignores_forwarded_when_not_trusted() {
1801 let uri: http::Uri = "/test".parse().unwrap();
1802 let builder = http::Request::builder()
1803 .method(Method::GET)
1804 .uri(uri)
1805 .header("x-forwarded-for", "192.168.1.100");
1806 let req = builder.body(()).unwrap();
1807 let (mut parts, _) = req.into_parts();
1808
1809 let socket_addr = std::net::SocketAddr::new(
1810 std::net::IpAddr::V4(std::net::Ipv4Addr::new(10, 0, 0, 1)),
1811 8080,
1812 );
1813 parts.extensions.insert(socket_addr);
1814
1815 let request = Request::new(
1816 parts,
1817 crate::request::BodyVariant::Buffered(Bytes::new()),
1818 Arc::new(Extensions::new()),
1819 PathParams::new(),
1820 );
1821
1822 let ip = ClientIp::extract_with_config(&request, false).unwrap();
1823 assert_eq!(ip.0, "10.0.0.1".parse::<std::net::IpAddr>().unwrap());
1824 }
1825
1826 #[test]
1827 fn test_extension_extractor_present() {
1828 #[derive(Clone, Debug, PartialEq)]
1829 struct MyData(String);
1830
1831 let request =
1832 create_test_request_with_extensions(Method::GET, "/test", MyData("hello".to_string()));
1833
1834 let result = Extension::<MyData>::from_request_parts(&request);
1835 assert!(result.is_ok());
1836 assert_eq!(result.unwrap().0, MyData("hello".to_string()));
1837 }
1838
1839 #[test]
1840 fn test_extension_extractor_missing() {
1841 #[derive(Clone, Debug)]
1842 #[allow(dead_code)]
1843 struct MyData(String);
1844
1845 let request = create_test_request_with_headers(Method::GET, "/test", vec![]);
1846
1847 let result = Extension::<MyData>::from_request_parts(&request);
1848 assert!(result.is_err());
1849 }
1850
1851 #[cfg(feature = "cookies")]
1853 mod cookies_tests {
1854 use super::*;
1855
1856 proptest! {
1864 #![proptest_config(ProptestConfig::with_cases(100))]
1865
1866 #[test]
1867 fn prop_cookies_extractor_parsing(
1868 cookies in prop::collection::vec(
1871 (
1872 "[a-zA-Z][a-zA-Z0-9_]{0,15}", "[a-zA-Z0-9]{1,30}" ),
1875 0..5
1876 )
1877 ) {
1878 let result: Result<(), TestCaseError> = (|| {
1879 let cookie_header = cookies
1881 .iter()
1882 .map(|(name, value)| format!("{}={}", name, value))
1883 .collect::<Vec<_>>()
1884 .join("; ");
1885
1886 let headers = if !cookies.is_empty() {
1887 vec![("cookie", cookie_header.as_str())]
1888 } else {
1889 vec![]
1890 };
1891
1892 let request = create_test_request_with_headers(Method::GET, "/test", headers);
1893
1894 let extracted = Cookies::from_request_parts(&request)
1896 .map_err(|e| TestCaseError::fail(format!("Failed to extract cookies: {}", e)))?;
1897
1898 let mut expected_cookies: std::collections::HashMap<&str, &str> = std::collections::HashMap::new();
1900 for (name, value) in &cookies {
1901 expected_cookies.insert(name.as_str(), value.as_str());
1902 }
1903
1904 for (name, expected_value) in &expected_cookies {
1906 let cookie = extracted.get(name)
1907 .ok_or_else(|| TestCaseError::fail(format!("Cookie '{}' not found", name)))?;
1908
1909 prop_assert_eq!(
1910 cookie.value(),
1911 *expected_value,
1912 "Cookie '{}' value mismatch",
1913 name
1914 );
1915 }
1916
1917 let extracted_count = extracted.iter().count();
1919 prop_assert_eq!(
1920 extracted_count,
1921 expected_cookies.len(),
1922 "Expected {} unique cookies, got {}",
1923 expected_cookies.len(),
1924 extracted_count
1925 );
1926
1927 Ok(())
1928 })();
1929 result?;
1930 }
1931 }
1932
1933 #[test]
1934 fn test_cookies_extractor_basic() {
1935 let request = create_test_request_with_headers(
1936 Method::GET,
1937 "/test",
1938 vec![("cookie", "session=abc123; user=john")],
1939 );
1940
1941 let cookies = Cookies::from_request_parts(&request).unwrap();
1942
1943 assert!(cookies.contains("session"));
1944 assert!(cookies.contains("user"));
1945 assert!(!cookies.contains("other"));
1946
1947 assert_eq!(cookies.get("session").unwrap().value(), "abc123");
1948 assert_eq!(cookies.get("user").unwrap().value(), "john");
1949 }
1950
1951 #[test]
1952 fn test_cookies_extractor_empty() {
1953 let request = create_test_request_with_headers(Method::GET, "/test", vec![]);
1954
1955 let cookies = Cookies::from_request_parts(&request).unwrap();
1956 assert_eq!(cookies.iter().count(), 0);
1957 }
1958
1959 #[test]
1960 fn test_cookies_extractor_single() {
1961 let request = create_test_request_with_headers(
1962 Method::GET,
1963 "/test",
1964 vec![("cookie", "token=xyz789")],
1965 );
1966
1967 let cookies = Cookies::from_request_parts(&request).unwrap();
1968 assert_eq!(cookies.iter().count(), 1);
1969 assert_eq!(cookies.get("token").unwrap().value(), "xyz789");
1970 }
1971 }
1972
1973 #[tokio::test]
1974 async fn test_async_validated_json_with_state_context() {
1975 use async_trait::async_trait;
1976 use rustapi_validate::prelude::*;
1977 use rustapi_validate::v2::{
1978 AsyncValidationRule, DatabaseValidator, ValidationContextBuilder,
1979 };
1980 use serde::{Deserialize, Serialize};
1981
1982 struct MockDbValidator {
1983 unique_values: Vec<String>,
1984 }
1985
1986 #[async_trait]
1987 impl DatabaseValidator for MockDbValidator {
1988 async fn exists(
1989 &self,
1990 _table: &str,
1991 _column: &str,
1992 _value: &str,
1993 ) -> Result<bool, String> {
1994 Ok(true)
1995 }
1996 async fn is_unique(
1997 &self,
1998 _table: &str,
1999 _column: &str,
2000 value: &str,
2001 ) -> Result<bool, String> {
2002 Ok(!self.unique_values.contains(&value.to_string()))
2003 }
2004 async fn is_unique_except(
2005 &self,
2006 _table: &str,
2007 _column: &str,
2008 value: &str,
2009 _except_id: &str,
2010 ) -> Result<bool, String> {
2011 Ok(!self.unique_values.contains(&value.to_string()))
2012 }
2013 }
2014
2015 #[derive(Debug, Deserialize, Serialize)]
2016 struct TestUser {
2017 email: String,
2018 }
2019
2020 impl Validate for TestUser {
2021 fn validate_with_group(
2022 &self,
2023 _group: rustapi_validate::v2::ValidationGroup,
2024 ) -> Result<(), rustapi_validate::v2::ValidationErrors> {
2025 Ok(())
2026 }
2027 }
2028
2029 #[async_trait]
2030 impl AsyncValidate for TestUser {
2031 async fn validate_async_with_group(
2032 &self,
2033 ctx: &ValidationContext,
2034 _group: rustapi_validate::v2::ValidationGroup,
2035 ) -> Result<(), rustapi_validate::v2::ValidationErrors> {
2036 let mut errors = rustapi_validate::v2::ValidationErrors::new();
2037
2038 let rule = AsyncUniqueRule::new("users", "email");
2039 if let Err(e) = rule.validate_async(&self.email, ctx).await {
2040 errors.add("email", e);
2041 }
2042
2043 errors.into_result()
2044 }
2045 }
2046
2047 let uri: http::Uri = "/test".parse().unwrap();
2049 let user = TestUser {
2050 email: "new@example.com".to_string(),
2051 };
2052 let body_bytes = serde_json::to_vec(&user).unwrap();
2053
2054 let builder = http::Request::builder()
2055 .method(Method::POST)
2056 .uri(uri.clone())
2057 .header("content-type", "application/json");
2058 let req = builder.body(()).unwrap();
2059 let (parts, _) = req.into_parts();
2060
2061 let mut request = Request::new(
2063 parts,
2064 crate::request::BodyVariant::Buffered(Bytes::from(body_bytes.clone())),
2065 Arc::new(Extensions::new()),
2066 PathParams::new(),
2067 );
2068
2069 let result = AsyncValidatedJson::<TestUser>::from_request(&mut request).await;
2070
2071 assert!(result.is_err(), "Expected error when validator is missing");
2072 let err = result.unwrap_err();
2073 let err_str = format!("{:?}", err);
2074 assert!(
2075 err_str.contains("Database validator not configured")
2076 || err_str.contains("async_unique"),
2077 "Error should mention missing configuration or rule: {:?}",
2078 err_str
2079 );
2080
2081 let db_validator = MockDbValidator {
2083 unique_values: vec!["taken@example.com".to_string()],
2084 };
2085 let ctx = ValidationContextBuilder::new()
2086 .database(db_validator)
2087 .build();
2088
2089 let mut extensions = Extensions::new();
2090 extensions.insert(ctx);
2091
2092 let builder = http::Request::builder()
2093 .method(Method::POST)
2094 .uri(uri.clone())
2095 .header("content-type", "application/json");
2096 let req = builder.body(()).unwrap();
2097 let (parts, _) = req.into_parts();
2098
2099 let mut request = Request::new(
2100 parts,
2101 crate::request::BodyVariant::Buffered(Bytes::from(body_bytes.clone())),
2102 Arc::new(extensions),
2103 PathParams::new(),
2104 );
2105
2106 let result = AsyncValidatedJson::<TestUser>::from_request(&mut request).await;
2107 assert!(
2108 result.is_ok(),
2109 "Expected success when validator is present and value is unique. Error: {:?}",
2110 result.err()
2111 );
2112
2113 let user_taken = TestUser {
2115 email: "taken@example.com".to_string(),
2116 };
2117 let body_taken = serde_json::to_vec(&user_taken).unwrap();
2118
2119 let db_validator = MockDbValidator {
2120 unique_values: vec!["taken@example.com".to_string()],
2121 };
2122 let ctx = ValidationContextBuilder::new()
2123 .database(db_validator)
2124 .build();
2125
2126 let mut extensions = Extensions::new();
2127 extensions.insert(ctx);
2128
2129 let builder = http::Request::builder()
2130 .method(Method::POST)
2131 .uri("/test")
2132 .header("content-type", "application/json");
2133 let req = builder.body(()).unwrap();
2134 let (parts, _) = req.into_parts();
2135
2136 let mut request = Request::new(
2137 parts,
2138 crate::request::BodyVariant::Buffered(Bytes::from(body_taken)),
2139 Arc::new(extensions),
2140 PathParams::new(),
2141 );
2142
2143 let result = AsyncValidatedJson::<TestUser>::from_request(&mut request).await;
2144 assert!(result.is_err(), "Expected validation error for taken email");
2145 }
2146}