prima_bridge/request/
body.rs1use std::borrow::Cow;
2
3use reqwest::multipart::Part;
4use serde::Serialize;
5
6use crate::prelude::{PrimaBridgeError, PrimaBridgeResult};
7
8#[derive(Debug)]
9pub struct Body {
15 pub(crate) inner: reqwest::Body,
16}
17
18impl Body {
19 pub fn from_stream<S>(stream: S) -> Body
26 where
27 S: futures::stream::TryStream + Send + Sync + 'static,
28 S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
29 bytes::Bytes: From<S::Ok>,
30 {
31 Self {
32 inner: reqwest::Body::wrap_stream(stream),
33 }
34 }
35
36 pub fn as_bytes(&self) -> Option<&[u8]> {
40 self.inner.as_bytes()
41 }
42
43 #[cfg(test)]
44 pub(crate) fn as_str(&self) -> Option<Cow<'_, str>> {
45 self.inner.as_bytes().map(String::from_utf8_lossy)
46 }
47}
48
49impl From<String> for Body {
50 fn from(val: String) -> Self {
51 Self {
52 inner: val.into_bytes().into(),
53 }
54 }
55}
56
57impl From<&'static str> for Body {
58 fn from(val: &'static str) -> Self {
59 Self {
60 inner: val.to_string().into_bytes().into(),
61 }
62 }
63}
64
65impl From<&'static [u8]> for Body {
66 fn from(val: &'static [u8]) -> Self {
67 Self {
68 inner: val.to_vec().into(),
69 }
70 }
71}
72
73impl From<Vec<u8>> for Body {
74 fn from(value: Vec<u8>) -> Self {
75 Self { inner: value.into() }
76 }
77}
78
79impl From<tokio::fs::File> for Body {
80 fn from(file: tokio::fs::File) -> Self {
81 Self { inner: file.into() }
82 }
83}
84
85#[allow(clippy::upper_case_acronyms)]
86#[derive(Debug, Serialize)]
87#[cfg_attr(test, derive(serde::Deserialize))]
88pub struct GraphQLBody<T> {
89 pub(crate) query: String,
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub(crate) variables: Option<T>,
92}
93
94impl<T: Serialize> From<(&str, Option<T>)> for GraphQLBody<T> {
95 fn from((query, variables): (&str, Option<T>)) -> Self {
96 Self {
97 query: query.to_owned(),
98 variables,
99 }
100 }
101}
102
103impl<T: Serialize> From<(String, Option<T>)> for GraphQLBody<T> {
104 fn from((query, variables): (String, Option<T>)) -> Self {
105 (query.as_str(), variables).into()
106 }
107}
108
109impl<T: Serialize> From<(String, T)> for GraphQLBody<T> {
110 fn from((query, variables): (String, T)) -> Self {
111 (query.as_str(), Some(variables)).into()
112 }
113}
114
115#[derive(Debug)]
116pub struct MultipartFile {
118 pub(crate) content: Body,
119 pub(crate) name_opt: Option<String>,
120 pub(crate) mime_type_opt: Option<String>,
121}
122
123impl MultipartFile {
124 pub fn new(content: impl Into<Body>) -> Self {
125 Self {
126 content: content.into(),
127 name_opt: None,
128 mime_type_opt: None,
129 }
130 }
131
132 pub fn with_name(self, name: impl Into<String>) -> Self {
133 Self {
134 name_opt: Some(name.into()),
135 ..self
136 }
137 }
138
139 pub fn with_mime_type(self, mime_type: impl Into<String>) -> Self {
140 Self {
141 mime_type_opt: Some(mime_type.into()),
142 ..self
143 }
144 }
145
146 pub(crate) fn into_part(self) -> PrimaBridgeResult<Part> {
147 let mut part = Part::stream(self.content.inner);
148 if let Some(name) = self.name_opt {
149 part = part.file_name(name);
150 }
151 if let Some(mime) = self.mime_type_opt {
152 part = part
153 .mime_str(mime.as_str())
154 .map_err(|_| PrimaBridgeError::InvalidMultipartFileMimeType(mime.to_string()))?;
155 }
156 Ok(part)
157 }
158}
159
160#[derive(Debug)]
161pub struct MultipartFormFileField {
163 pub(crate) field_name: Cow<'static, str>,
164 pub(crate) file: MultipartFile,
165}
166impl MultipartFormFileField {
167 pub fn new<S>(file_name: S, file: MultipartFile) -> Self
168 where
169 S: Into<Cow<'static, str>>,
170 {
171 Self {
172 field_name: file_name.into(),
173 file,
174 }
175 }
176}
177impl std::hash::Hash for MultipartFormFileField {
178 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
179 self.field_name.hash(state);
180 }
181}
182impl Eq for MultipartFormFileField {}
183impl PartialEq for MultipartFormFileField {
184 fn eq(&self, other: &Self) -> bool {
185 self.field_name == other.field_name
186 }
187}