poem_openapi/types/multipart/
upload.rs1use std::{
2 borrow::Cow,
3 fmt::{self, Debug, Formatter},
4};
5
6use poem::web::Field as PoemField;
7use tokio::{
8 fs::File,
9 io::{AsyncRead, AsyncReadExt, AsyncSeek, Error as IoError, ErrorKind},
10};
11
12use crate::{
13 registry::{MetaSchema, MetaSchemaRef},
14 types::{ParseError, ParseFromMultipartField, ParseResult, Type},
15};
16
17pub struct Upload {
19 file_name: Option<String>,
20 content_type: Option<String>,
21 file: File,
22 size: usize,
23}
24
25impl Debug for Upload {
26 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
27 let mut d = f.debug_struct("Upload");
28 if let Some(file_name) = self.file_name() {
29 d.field("filename", &file_name);
30 }
31 if let Some(content_type) = self.content_type() {
32 d.field("content_type", &content_type);
33 }
34 d.finish()
35 }
36}
37
38impl Upload {
39 #[inline]
41 pub fn content_type(&self) -> Option<&str> {
42 self.content_type.as_deref()
43 }
44
45 #[inline]
47 pub fn file_name(&self) -> Option<&str> {
48 self.file_name.as_deref()
49 }
50
51 #[inline]
53 pub fn size(&self) -> usize {
54 self.size
55 }
56
57 pub async fn into_vec(self) -> Result<Vec<u8>, IoError> {
60 let mut data = Vec::new();
61 self.into_async_read().read_to_end(&mut data).await?;
62 Ok(data)
63 }
64
65 pub async fn into_string(self) -> Result<String, IoError> {
67 String::from_utf8(
68 self.into_vec()
69 .await
70 .map_err(|err| IoError::new(ErrorKind::Other, err))?
71 .to_vec(),
72 )
73 .map_err(|err| IoError::new(ErrorKind::Other, err))
74 }
75
76 pub fn into_async_read(self) -> impl AsyncRead + AsyncSeek + Unpin + Send + 'static {
78 self.file
79 }
80
81 pub fn into_file(self) -> File {
83 self.file
84 }
85}
86
87impl Type for Upload {
88 const IS_REQUIRED: bool = true;
89
90 type RawValueType = Self;
91
92 type RawElementValueType = Self;
93
94 fn name() -> Cow<'static, str> {
95 "string_binary".into()
96 }
97
98 fn schema_ref() -> MetaSchemaRef {
99 MetaSchemaRef::Inline(Box::new(MetaSchema::new_with_format("string", "binary")))
100 }
101
102 fn as_raw_value(&self) -> Option<&Self::RawValueType> {
103 Some(self)
104 }
105
106 fn raw_element_iter<'a>(
107 &'a self,
108 ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
109 Box::new(self.as_raw_value().into_iter())
110 }
111}
112
113impl ParseFromMultipartField for Upload {
114 async fn parse_from_multipart(field: Option<PoemField>) -> ParseResult<Self> {
115 match field {
116 Some(field) => {
117 let content_type = field.content_type().map(ToString::to_string);
118 let file_name = field.file_name().map(ToString::to_string);
119 let file = field.tempfile().await.map_err(ParseError::custom)?;
120 let size = file.metadata().await.map_err(ParseError::custom)?.len() as usize;
121 Ok(Self {
122 content_type,
123 file_name,
124 file,
125 size,
126 })
127 }
128 None => Err(ParseError::expected_input()),
129 }
130 }
131}