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 OperationModifier for Headers {
714 fn update_operation(_op: &mut Operation) {}
715}
716
717impl Deref for Headers {
718 type Target = http::HeaderMap;
719
720 fn deref(&self) -> &Self::Target {
721 &self.0
722 }
723}
724
725#[derive(Debug, Clone)]
744pub struct HeaderValue(pub String, pub &'static str);
745
746impl HeaderValue {
747 pub fn new(name: &'static str, value: String) -> Self {
749 Self(value, name)
750 }
751
752 pub fn value(&self) -> &str {
754 &self.0
755 }
756
757 pub fn name(&self) -> &'static str {
759 self.1
760 }
761
762 pub fn extract(req: &Request, name: &'static str) -> Result<Self> {
764 req.headers()
765 .get(name)
766 .and_then(|v| v.to_str().ok())
767 .map(|s| HeaderValue(s.to_string(), name))
768 .ok_or_else(|| ApiError::bad_request(format!("Missing required header: {}", name)))
769 }
770}
771
772impl Deref for HeaderValue {
773 type Target = String;
774
775 fn deref(&self) -> &Self::Target {
776 &self.0
777 }
778}
779
780#[derive(Debug, Clone)]
798pub struct Extension<T>(pub T);
799
800impl<T: Clone + Send + Sync + 'static> FromRequestParts for Extension<T> {
801 fn from_request_parts(req: &Request) -> Result<Self> {
802 req.extensions()
803 .get::<T>()
804 .cloned()
805 .map(Extension)
806 .ok_or_else(|| {
807 ApiError::internal(format!(
808 "Extension of type `{}` not found. Did middleware insert it?",
809 std::any::type_name::<T>()
810 ))
811 })
812 }
813}
814
815impl<T> Deref for Extension<T> {
816 type Target = T;
817
818 fn deref(&self) -> &Self::Target {
819 &self.0
820 }
821}
822
823impl<T> DerefMut for Extension<T> {
824 fn deref_mut(&mut self) -> &mut Self::Target {
825 &mut self.0
826 }
827}
828
829#[derive(Debug, Clone)]
844pub struct ClientIp(pub std::net::IpAddr);
845
846impl ClientIp {
847 pub fn extract_with_config(req: &Request, trust_proxy: bool) -> Result<Self> {
849 if trust_proxy {
850 if let Some(forwarded) = req.headers().get("x-forwarded-for") {
852 if let Ok(forwarded_str) = forwarded.to_str() {
853 if let Some(first_ip) = forwarded_str.split(',').next() {
855 if let Ok(ip) = first_ip.trim().parse() {
856 return Ok(ClientIp(ip));
857 }
858 }
859 }
860 }
861 }
862
863 if let Some(addr) = req.extensions().get::<std::net::SocketAddr>() {
865 return Ok(ClientIp(addr.ip()));
866 }
867
868 Ok(ClientIp(std::net::IpAddr::V4(std::net::Ipv4Addr::new(
870 127, 0, 0, 1,
871 ))))
872 }
873}
874
875impl FromRequestParts for ClientIp {
876 fn from_request_parts(req: &Request) -> Result<Self> {
877 Self::extract_with_config(req, true)
879 }
880}
881
882#[cfg(feature = "cookies")]
900#[derive(Debug, Clone)]
901pub struct Cookies(pub cookie::CookieJar);
902
903#[cfg(feature = "cookies")]
904impl Cookies {
905 pub fn get(&self, name: &str) -> Option<&cookie::Cookie<'static>> {
907 self.0.get(name)
908 }
909
910 pub fn iter(&self) -> impl Iterator<Item = &cookie::Cookie<'static>> {
912 self.0.iter()
913 }
914
915 pub fn contains(&self, name: &str) -> bool {
917 self.0.get(name).is_some()
918 }
919}
920
921#[cfg(feature = "cookies")]
922impl FromRequestParts for Cookies {
923 fn from_request_parts(req: &Request) -> Result<Self> {
924 let mut jar = cookie::CookieJar::new();
925
926 if let Some(cookie_header) = req.headers().get(header::COOKIE) {
927 if let Ok(cookie_str) = cookie_header.to_str() {
928 for cookie_part in cookie_str.split(';') {
930 let trimmed = cookie_part.trim();
931 if !trimmed.is_empty() {
932 if let Ok(cookie) = cookie::Cookie::parse(trimmed.to_string()) {
933 jar.add_original(cookie.into_owned());
934 }
935 }
936 }
937 }
938 }
939
940 Ok(Cookies(jar))
941 }
942}
943
944#[cfg(feature = "cookies")]
945impl Deref for Cookies {
946 type Target = cookie::CookieJar;
947
948 fn deref(&self) -> &Self::Target {
949 &self.0
950 }
951}
952
953macro_rules! impl_from_request_parts_for_primitives {
955 ($($ty:ty),*) => {
956 $(
957 impl FromRequestParts for $ty {
958 fn from_request_parts(req: &Request) -> Result<Self> {
959 let Path(value) = Path::<$ty>::from_request_parts(req)?;
960 Ok(value)
961 }
962 }
963 )*
964 };
965}
966
967impl_from_request_parts_for_primitives!(
968 i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64, bool, String
969);
970
971use rustapi_openapi::{
974 MediaType, Operation, OperationModifier, Parameter, RequestBody, ResponseModifier, ResponseSpec,
975};
976
977impl<T: RustApiSchema> OperationModifier for ValidatedJson<T> {
979 fn update_operation(op: &mut Operation) {
980 let mut ctx = SchemaCtx::new();
981 let schema_ref = T::schema(&mut ctx);
982
983 let mut content = BTreeMap::new();
984 content.insert(
985 "application/json".to_string(),
986 MediaType {
987 schema: Some(schema_ref),
988 example: None,
989 },
990 );
991
992 op.request_body = Some(RequestBody {
993 description: None,
994 required: Some(true),
995 content,
996 });
997
998 let mut responses_content = BTreeMap::new();
1000 responses_content.insert(
1001 "application/json".to_string(),
1002 MediaType {
1003 schema: Some(SchemaRef::Ref {
1004 reference: "#/components/schemas/ValidationErrorSchema".to_string(),
1005 }),
1006 example: None,
1007 },
1008 );
1009
1010 op.responses.insert(
1011 "422".to_string(),
1012 ResponseSpec {
1013 description: "Validation Error".to_string(),
1014 content: responses_content,
1015 headers: BTreeMap::new(),
1016 },
1017 );
1018 }
1019
1020 fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
1021 spec.register_in_place::<T>();
1022 spec.register_in_place::<rustapi_openapi::ValidationErrorSchema>();
1023 spec.register_in_place::<rustapi_openapi::ValidationErrorBodySchema>();
1024 spec.register_in_place::<rustapi_openapi::FieldErrorSchema>();
1025 }
1026}
1027
1028impl<T: RustApiSchema> OperationModifier for AsyncValidatedJson<T> {
1030 fn update_operation(op: &mut Operation) {
1031 let mut ctx = SchemaCtx::new();
1032 let schema_ref = T::schema(&mut ctx);
1033
1034 let mut content = BTreeMap::new();
1035 content.insert(
1036 "application/json".to_string(),
1037 MediaType {
1038 schema: Some(schema_ref),
1039 example: None,
1040 },
1041 );
1042
1043 op.request_body = Some(RequestBody {
1044 description: None,
1045 required: Some(true),
1046 content,
1047 });
1048
1049 let mut responses_content = BTreeMap::new();
1051 responses_content.insert(
1052 "application/json".to_string(),
1053 MediaType {
1054 schema: Some(SchemaRef::Ref {
1055 reference: "#/components/schemas/ValidationErrorSchema".to_string(),
1056 }),
1057 example: None,
1058 },
1059 );
1060
1061 op.responses.insert(
1062 "422".to_string(),
1063 ResponseSpec {
1064 description: "Validation Error".to_string(),
1065 content: responses_content,
1066 headers: BTreeMap::new(),
1067 },
1068 );
1069 }
1070
1071 fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
1072 spec.register_in_place::<T>();
1073 spec.register_in_place::<rustapi_openapi::ValidationErrorSchema>();
1074 spec.register_in_place::<rustapi_openapi::ValidationErrorBodySchema>();
1075 spec.register_in_place::<rustapi_openapi::FieldErrorSchema>();
1076 }
1077}
1078
1079impl<T: RustApiSchema> OperationModifier for Json<T> {
1081 fn update_operation(op: &mut Operation) {
1082 let mut ctx = SchemaCtx::new();
1083 let schema_ref = T::schema(&mut ctx);
1084
1085 let mut content = BTreeMap::new();
1086 content.insert(
1087 "application/json".to_string(),
1088 MediaType {
1089 schema: Some(schema_ref),
1090 example: None,
1091 },
1092 );
1093
1094 op.request_body = Some(RequestBody {
1095 description: None,
1096 required: Some(true),
1097 content,
1098 });
1099 }
1100
1101 fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
1102 spec.register_in_place::<T>();
1103 }
1104}
1105
1106impl<T> OperationModifier for Path<T> {
1108 fn update_operation(_op: &mut Operation) {}
1109}
1110
1111impl<T> OperationModifier for Typed<T> {
1113 fn update_operation(_op: &mut Operation) {}
1114}
1115
1116impl<T: RustApiSchema> OperationModifier for Query<T> {
1118 fn update_operation(op: &mut Operation) {
1119 let mut ctx = SchemaCtx::new();
1120 if let Some(fields) = T::field_schemas(&mut ctx) {
1121 let new_params: Vec<Parameter> = fields
1122 .into_iter()
1123 .map(|(name, schema)| {
1124 Parameter {
1125 name,
1126 location: "query".to_string(),
1127 required: false, deprecated: None,
1129 description: None,
1130 schema: Some(schema),
1131 }
1132 })
1133 .collect();
1134
1135 op.parameters.extend(new_params);
1136 }
1137 }
1138
1139 fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
1140 spec.register_in_place::<T>();
1141 }
1142}
1143
1144impl<T> OperationModifier for State<T> {
1146 fn update_operation(_op: &mut Operation) {}
1147}
1148
1149impl OperationModifier for Body {
1151 fn update_operation(op: &mut Operation) {
1152 let mut content = BTreeMap::new();
1153 content.insert(
1154 "application/octet-stream".to_string(),
1155 MediaType {
1156 schema: Some(SchemaRef::Inline(
1157 serde_json::json!({ "type": "string", "format": "binary" }),
1158 )),
1159 example: None,
1160 },
1161 );
1162
1163 op.request_body = Some(RequestBody {
1164 description: None,
1165 required: Some(true),
1166 content,
1167 });
1168 }
1169}
1170
1171impl OperationModifier for BodyStream {
1173 fn update_operation(op: &mut Operation) {
1174 let mut content = BTreeMap::new();
1175 content.insert(
1176 "application/octet-stream".to_string(),
1177 MediaType {
1178 schema: Some(SchemaRef::Inline(
1179 serde_json::json!({ "type": "string", "format": "binary" }),
1180 )),
1181 example: None,
1182 },
1183 );
1184
1185 op.request_body = Some(RequestBody {
1186 description: None,
1187 required: Some(true),
1188 content,
1189 });
1190 }
1191}
1192
1193impl<T: RustApiSchema> ResponseModifier for Json<T> {
1197 fn update_response(op: &mut Operation) {
1198 let mut ctx = SchemaCtx::new();
1199 let schema_ref = T::schema(&mut ctx);
1200
1201 let mut content = BTreeMap::new();
1202 content.insert(
1203 "application/json".to_string(),
1204 MediaType {
1205 schema: Some(schema_ref),
1206 example: None,
1207 },
1208 );
1209
1210 op.responses.insert(
1211 "200".to_string(),
1212 ResponseSpec {
1213 description: "Successful response".to_string(),
1214 content,
1215 headers: BTreeMap::new(),
1216 },
1217 );
1218 }
1219
1220 fn register_components(spec: &mut rustapi_openapi::OpenApiSpec) {
1221 spec.register_in_place::<T>();
1222 }
1223}
1224
1225impl<T: RustApiSchema> RustApiSchema for Json<T> {
1228 fn schema(ctx: &mut SchemaCtx) -> SchemaRef {
1229 T::schema(ctx)
1230 }
1231}
1232
1233impl<T: RustApiSchema> RustApiSchema for ValidatedJson<T> {
1234 fn schema(ctx: &mut SchemaCtx) -> SchemaRef {
1235 T::schema(ctx)
1236 }
1237}
1238
1239impl<T: RustApiSchema> RustApiSchema for AsyncValidatedJson<T> {
1240 fn schema(ctx: &mut SchemaCtx) -> SchemaRef {
1241 T::schema(ctx)
1242 }
1243}
1244
1245impl<T: RustApiSchema> RustApiSchema for Query<T> {
1246 fn schema(ctx: &mut SchemaCtx) -> SchemaRef {
1247 T::schema(ctx)
1248 }
1249 fn field_schemas(ctx: &mut SchemaCtx) -> Option<BTreeMap<String, SchemaRef>> {
1250 T::field_schemas(ctx)
1251 }
1252}
1253
1254const DEFAULT_PAGE: u64 = 1;
1258const DEFAULT_PER_PAGE: u64 = 20;
1260const MAX_PER_PAGE: u64 = 100;
1262
1263#[derive(Debug, Clone, Copy)]
1280pub struct Paginate {
1281 pub page: u64,
1283 pub per_page: u64,
1285}
1286
1287impl Paginate {
1288 pub fn new(page: u64, per_page: u64) -> Self {
1290 Self {
1291 page: page.max(1),
1292 per_page: per_page.clamp(1, MAX_PER_PAGE),
1293 }
1294 }
1295
1296 pub fn offset(&self) -> u64 {
1298 (self.page - 1) * self.per_page
1299 }
1300
1301 pub fn limit(&self) -> u64 {
1303 self.per_page
1304 }
1305
1306 pub fn paginate<T>(self, items: Vec<T>, total: u64) -> crate::hateoas::Paginated<T> {
1308 crate::hateoas::Paginated {
1309 items,
1310 page: self.page,
1311 per_page: self.per_page,
1312 total,
1313 }
1314 }
1315}
1316
1317impl Default for Paginate {
1318 fn default() -> Self {
1319 Self {
1320 page: DEFAULT_PAGE,
1321 per_page: DEFAULT_PER_PAGE,
1322 }
1323 }
1324}
1325
1326impl FromRequestParts for Paginate {
1327 fn from_request_parts(req: &Request) -> Result<Self> {
1328 let query = req.query_string().unwrap_or("");
1329
1330 #[derive(serde::Deserialize)]
1331 struct PaginateQuery {
1332 page: Option<u64>,
1333 per_page: Option<u64>,
1334 }
1335
1336 let params: PaginateQuery = serde_urlencoded::from_str(query).unwrap_or(PaginateQuery {
1337 page: None,
1338 per_page: None,
1339 });
1340
1341 Ok(Paginate::new(
1342 params.page.unwrap_or(DEFAULT_PAGE),
1343 params.per_page.unwrap_or(DEFAULT_PER_PAGE),
1344 ))
1345 }
1346}
1347
1348#[derive(Debug, Clone)]
1371pub struct CursorPaginate {
1372 pub cursor: Option<String>,
1374 pub per_page: u64,
1376}
1377
1378impl CursorPaginate {
1379 pub fn new(cursor: Option<String>, per_page: u64) -> Self {
1381 Self {
1382 cursor,
1383 per_page: per_page.clamp(1, MAX_PER_PAGE),
1384 }
1385 }
1386
1387 pub fn after(&self) -> Option<&str> {
1389 self.cursor.as_deref()
1390 }
1391
1392 pub fn limit(&self) -> u64 {
1394 self.per_page
1395 }
1396
1397 pub fn is_first_page(&self) -> bool {
1399 self.cursor.is_none()
1400 }
1401}
1402
1403impl Default for CursorPaginate {
1404 fn default() -> Self {
1405 Self {
1406 cursor: None,
1407 per_page: DEFAULT_PER_PAGE,
1408 }
1409 }
1410}
1411
1412impl FromRequestParts for CursorPaginate {
1413 fn from_request_parts(req: &Request) -> Result<Self> {
1414 let query = req.query_string().unwrap_or("");
1415
1416 #[derive(serde::Deserialize)]
1417 struct CursorQuery {
1418 cursor: Option<String>,
1419 limit: Option<u64>,
1420 }
1421
1422 let params: CursorQuery = serde_urlencoded::from_str(query).unwrap_or(CursorQuery {
1423 cursor: None,
1424 limit: None,
1425 });
1426
1427 Ok(CursorPaginate::new(
1428 params.cursor,
1429 params.limit.unwrap_or(DEFAULT_PER_PAGE),
1430 ))
1431 }
1432}
1433
1434#[cfg(test)]
1435mod tests {
1436 include!(concat!(
1437 env!("CARGO_MANIFEST_DIR"),
1438 "/tests/support/extract_lib.rs"
1439 ));
1440}