poem_openapi/payload/
base64_payload.rs1use std::ops::{Deref, DerefMut};
2
3use base64::engine::{general_purpose::STANDARD, Engine};
4use bytes::Bytes;
5use futures_util::TryFutureExt;
6use poem::{IntoResponse, Request, RequestBody, Response, Result};
7
8use crate::{
9 error::ParseRequestPayloadError,
10 payload::{ParsePayload, Payload},
11 registry::{MetaMediaType, MetaResponse, MetaResponses, MetaSchema, MetaSchemaRef, Registry},
12 ApiResponse,
13};
14
15#[derive(Debug, Clone, Eq, PartialEq)]
72pub struct Base64<T>(pub T);
73
74impl<T> Deref for Base64<T> {
75 type Target = T;
76
77 fn deref(&self) -> &Self::Target {
78 &self.0
79 }
80}
81
82impl<T> DerefMut for Base64<T> {
83 fn deref_mut(&mut self) -> &mut Self::Target {
84 &mut self.0
85 }
86}
87
88impl<T: Send> Payload for Base64<T> {
89 const CONTENT_TYPE: &'static str = "text/plain; charset=utf-8";
90
91 fn check_content_type(content_type: &str) -> bool {
92 matches!(content_type.parse::<mime::Mime>(), Ok(content_type) if content_type.type_() == "text"
93 && (content_type.subtype() == "plain"
94 || content_type
95 .suffix()
96 .is_some_and(|v| v == "plain")))
97 }
98
99 fn schema_ref() -> MetaSchemaRef {
100 MetaSchemaRef::Inline(Box::new(MetaSchema {
101 format: Some("string"),
102 ..MetaSchema::new("bytes")
103 }))
104 }
105}
106
107async fn read_base64(body: &mut RequestBody) -> Result<Vec<u8>> {
108 let body = async move { body.take() }
109 .and_then(|body| body.into_vec())
110 .await
111 .map_err(|err| ParseRequestPayloadError {
112 reason: err.to_string(),
113 })?;
114 let data = STANDARD
115 .decode(body)
116 .map_err(|err| ParseRequestPayloadError {
117 reason: err.to_string(),
118 })?;
119 Ok(data)
120}
121
122impl ParsePayload for Base64<Vec<u8>> {
123 const IS_REQUIRED: bool = true;
124
125 async fn from_request(_request: &Request, body: &mut RequestBody) -> Result<Self> {
126 read_base64(body).await.map(Self)
127 }
128}
129
130impl ParsePayload for Base64<Bytes> {
131 const IS_REQUIRED: bool = true;
132
133 async fn from_request(_request: &Request, body: &mut RequestBody) -> Result<Self> {
134 read_base64(body).await.map(|data| Self(data.into()))
135 }
136}
137
138impl<T: AsRef<[u8]> + Send> IntoResponse for Base64<T> {
139 fn into_response(self) -> Response {
140 Response::builder()
141 .content_type(Self::CONTENT_TYPE)
142 .body(STANDARD.encode(self.0.as_ref()))
143 }
144}
145
146impl<T: AsRef<[u8]> + Send> ApiResponse for Base64<T> {
147 fn meta() -> MetaResponses {
148 MetaResponses {
149 responses: vec![MetaResponse {
150 description: "",
151 status: Some(200),
152 status_range: None,
153 content: vec![MetaMediaType {
154 content_type: Self::CONTENT_TYPE,
155 schema: Self::schema_ref(),
156 }],
157 headers: vec![],
158 }],
159 }
160 }
161
162 fn register(_registry: &mut Registry) {}
163}
164
165impl_apirequest_for_payload!(Base64<Vec<u8>>);
166impl_apirequest_for_payload!(Base64<Bytes>);