cat_dev/net/server/requestable/
body.rs

1//! Used for extracting the body from any particular request.
2
3use crate::{
4	errors::CatBridgeError,
5	net::models::{FromRequest, Request},
6};
7use bytes::Bytes;
8use miette::Report;
9use std::fmt::{Debug, Formatter, Result as FmtResult};
10use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
11
12/// Extract the body from a request.
13///
14/// Consumes the request, and must come last.
15pub struct Body<BodyTy>(pub BodyTy);
16
17impl<BodyTy, ErrTy, State: Clone + Send + Sync + 'static> FromRequest<State> for Body<BodyTy>
18where
19	BodyTy: TryFrom<Bytes, Error = ErrTy>,
20	ErrTy: Into<Report> + Send + Sync + 'static,
21{
22	async fn from_request(req: Request<State>) -> Result<Self, CatBridgeError> {
23		BodyTy::try_from(req.body_owned())
24			.map(Body)
25			.map_err(Into::into)
26			.map_err(CatBridgeError::UnknownError)
27	}
28}
29
30impl<BodyTy> Debug for Body<BodyTy>
31where
32	BodyTy: Debug,
33{
34	fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
35		fmt.debug_struct("Body").field("inner", &self.0).finish()
36	}
37}
38
39const BODY_FIELDS: &[NamedField<'static>] = &[NamedField::new("inner")];
40
41impl<BodyTy> Structable for Body<BodyTy>
42where
43	BodyTy: Valuable,
44{
45	fn definition(&self) -> StructDef<'_> {
46		StructDef::new_static("Body", Fields::Named(BODY_FIELDS))
47	}
48}
49
50impl<BodyTy> Valuable for Body<BodyTy>
51where
52	BodyTy: Valuable,
53{
54	fn as_value(&self) -> Value<'_> {
55		Value::Structable(self)
56	}
57
58	fn visit(&self, visitor: &mut dyn Visit) {
59		visitor.visit_named_fields(&NamedValues::new(BODY_FIELDS, &[self.0.as_value()]));
60	}
61}
62
63#[cfg(test)]
64mod unit_tests {
65	use super::*;
66	use crate::net::server::router::{Router, test_helpers::*};
67
68	#[tokio::test]
69	pub async fn test_extract_body() {
70		struct CustomTy(pub Vec<u8>);
71		impl CustomTy {
72			#[must_use]
73			pub fn to_added_bytes(self) -> Vec<u8> {
74				self.0.into_iter().map(|num| num + 1).collect::<Vec<_>>()
75			}
76		}
77		impl TryFrom<Bytes> for CustomTy {
78			type Error = CatBridgeError;
79
80			fn try_from(body: Bytes) -> Result<Self, Self::Error> {
81				let as_vec = Vec::<u8>::from(body);
82
83				assert_eq!(
84					as_vec,
85					vec![0x1, 0x2, 0x3],
86					"Body for custom type was not correct!"
87				);
88
89				Ok(Self(as_vec))
90			}
91		}
92
93		async fn echo_custom(Body(echo): Body<CustomTy>) -> Vec<u8> {
94			echo.to_added_bytes()
95		}
96		let mut router = Router::new();
97		router
98			.add_route(&[0x1], echo_custom)
99			.expect("Failed to add route!");
100		assert_eq!(
101			router_body_no_close(&mut router, &[0x1, 0x2, 0x3]).await,
102			&[0x2, 0x3, 0x4],
103		);
104	}
105}