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 include!(concat!(
1433 env!("CARGO_MANIFEST_DIR"),
1434 "/tests/support/extract_lib.rs"
1435 ));
1436}