1use mime::Mime;
2use serde::Serialize;
3
4#[derive(Clone, Default)]
9pub struct Body {
10 bytes: Vec<u8>,
11 mime: Option<Mime>,
12}
13
14impl Body {
15 #[must_use]
17 pub fn into_bytes(self) -> Vec<u8> {
18 self.bytes
19 }
20
21 #[must_use]
23 #[allow(clippy::missing_const_for_fn)]
24 pub fn mime(&self) -> Option<&Mime> {
25 self.mime.as_ref()
26 }
27
28 #[must_use]
30 #[allow(clippy::missing_const_for_fn)]
31 pub fn len(&self) -> usize {
32 self.bytes.len()
33 }
34
35 #[must_use]
37 #[allow(clippy::missing_const_for_fn)]
38 pub fn is_empty(&self) -> bool {
39 self.bytes.is_empty()
40 }
41
42 #[must_use]
44 #[allow(clippy::missing_const_for_fn)]
45 pub fn from_string(s: String) -> Self {
46 Self {
47 bytes: s.into_bytes(),
48 mime: Some(mime::TEXT_PLAIN_UTF_8),
49 }
50 }
51
52 pub fn from_json(value: &impl Serialize) -> Result<Self, serde_json::Error> {
57 let bytes = serde_json::to_vec(value)?;
58 Ok(Self {
59 bytes,
60 mime: Some(mime::APPLICATION_JSON),
61 })
62 }
63
64 pub fn from_form(value: &impl Serialize) -> Result<Self, serde_qs::Error> {
69 let bytes = serde_qs::to_string(value)?.into_bytes();
70 Ok(Self {
71 bytes,
72 mime: Some(mime::APPLICATION_WWW_FORM_URLENCODED),
73 })
74 }
75}
76
77impl From<String> for Body {
78 fn from(s: String) -> Self {
79 Self::from_string(s)
80 }
81}
82
83impl From<&str> for Body {
84 fn from(s: &str) -> Self {
85 Self::from_string(s.to_owned())
86 }
87}
88
89impl From<Vec<u8>> for Body {
90 fn from(bytes: Vec<u8>) -> Self {
91 Self {
92 bytes,
93 mime: Some(mime::APPLICATION_OCTET_STREAM),
94 }
95 }
96}
97
98impl<'a> From<&'a [u8]> for Body {
99 fn from(bytes: &'a [u8]) -> Self {
100 Self {
101 bytes: bytes.to_vec(),
102 mime: Some(mime::APPLICATION_OCTET_STREAM),
103 }
104 }
105}
106
107impl From<serde_json::Value> for Body {
108 fn from(value: serde_json::Value) -> Self {
109 let bytes = serde_json::to_vec(&value).unwrap_or_default();
111 Self {
112 bytes,
113 mime: Some(mime::APPLICATION_JSON),
114 }
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn default_body_is_empty() {
124 let body = Body::default();
125 assert!(body.is_empty());
126 assert_eq!(body.len(), 0);
127 assert!(body.mime().is_none());
128 }
129
130 #[test]
131 fn from_string_sets_text_plain_utf8() {
132 let body = Body::from("hello".to_string());
133 assert_eq!(body.into_bytes(), b"hello");
134 let body2 = Body::from_string("world".to_owned());
136 assert_eq!(body2.mime().unwrap(), &mime::TEXT_PLAIN_UTF_8);
137 }
138
139 #[test]
140 fn from_str_ref_sets_text_plain_utf8() {
141 let body = Body::from("hello");
142 assert_eq!(body.mime().unwrap(), &mime::TEXT_PLAIN_UTF_8);
143 }
144
145 #[test]
146 fn from_bytes_sets_octet_stream() {
147 let body = Body::from(vec![1u8, 2, 3]);
148 assert_eq!(body.mime().unwrap(), &mime::APPLICATION_OCTET_STREAM);
149 assert_eq!(body.into_bytes(), vec![1, 2, 3]);
150 }
151
152 #[test]
153 fn from_byte_slice_sets_octet_stream() {
154 let body = Body::from(&[4u8, 5, 6][..]);
155 assert_eq!(body.mime().unwrap(), &mime::APPLICATION_OCTET_STREAM);
156 }
157
158 #[test]
159 fn from_json_serialises_and_sets_application_json() {
160 #[derive(serde::Serialize)]
161 struct Foo {
162 x: u32,
163 }
164 let body = Body::from_json(&Foo { x: 42 }).unwrap();
165 assert_eq!(body.mime().unwrap(), &mime::APPLICATION_JSON);
166 let parsed: serde_json::Value = serde_json::from_slice(&body.into_bytes()).unwrap();
167 assert_eq!(parsed["x"], 42);
168 }
169
170 #[test]
171 fn from_json_value_sets_application_json() {
172 let val = serde_json::json!({"key": "value"});
173 let body = Body::from(val);
174 assert_eq!(body.mime().unwrap(), &mime::APPLICATION_JSON);
175 }
176
177 #[test]
178 fn from_form_serialises_and_sets_form_urlencoded() {
179 #[derive(serde::Serialize)]
180 struct Form {
181 name: String,
182 age: u32,
183 }
184 let body = Body::from_form(&Form {
185 name: "Alice".into(),
186 age: 30,
187 })
188 .unwrap();
189 assert_eq!(body.mime().unwrap(), &mime::APPLICATION_WWW_FORM_URLENCODED);
190 let s = String::from_utf8(body.into_bytes()).unwrap();
191 assert!(s.contains("name=Alice"));
192 assert!(s.contains("age=30"));
193 }
194}