diff --git a/src/requests/form_builder.rs b/src/requests/form_builder.rs index eeaa08d7..1f433035 100644 --- a/src/requests/form_builder.rs +++ b/src/requests/form_builder.rs @@ -3,7 +3,7 @@ use std::{borrow::Cow, path::PathBuf}; use reqwest::multipart::Form; use crate::{ - requests::utils::file_to_part, + requests::utils::{file_from_memory_to_part, file_to_part}, types::{ ChatId, InlineKeyboardMarkup, InputFile, InputMedia, MaskPosition, ParseMode, ReplyMarkup, @@ -33,6 +33,9 @@ impl FormBuilder { Self { form: self.form.text(name, string) } } Some(FormValue::File(path)) => self.add_file(name, path).await, + Some(FormValue::Memory { file_name, data }) => { + self.add_file_from_memory(name, file_name, data) + } None => self, } } @@ -50,6 +53,23 @@ impl FormBuilder { } } + fn add_file_from_memory<'a, N>( + self, + name: N, + file_name: String, + data: Cow<'static, [u8]>, + ) -> Self + where + N: Into<Cow<'a, str>>, + { + Self { + form: self.form.part( + name.into().into_owned(), + file_from_memory_to_part(data, file_name), + ), + } + } + pub fn build(self) -> Form { self.form } @@ -57,6 +77,7 @@ impl FormBuilder { pub(crate) enum FormValue { File(PathBuf), + Memory { file_name: String, data: Cow<'static, [u8]> }, Str(String), } @@ -153,6 +174,10 @@ impl IntoFormValue for InputFile { fn into_form_value(&self) -> Option<FormValue> { match self { InputFile::File(path) => Some(FormValue::File(path.clone())), + InputFile::Memory { file_name, data } => Some(FormValue::Memory { + file_name: file_name.clone(), + data: data.clone(), + }), InputFile::Url(url) => Some(FormValue::Str(url.clone())), InputFile::FileId(file_id) => Some(FormValue::Str(file_id.clone())), } diff --git a/src/requests/utils.rs b/src/requests/utils.rs index eab235ce..25cc5926 100644 --- a/src/requests/utils.rs +++ b/src/requests/utils.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{borrow::Cow, path::PathBuf}; use bytes::{Bytes, BytesMut}; use reqwest::{multipart::Part, Body}; @@ -34,3 +34,10 @@ pub async fn file_to_part(path_to_file: PathBuf) -> Part { Part::stream(Body::wrap_stream(file)).file_name(file_name) } + +pub fn file_from_memory_to_part( + data: Cow<'static, [u8]>, + name: String, +) -> Part { + Part::bytes(data).file_name(name) +} diff --git a/src/types/input_file.rs b/src/types/input_file.rs index 8c6264a5..83b7fe9c 100644 --- a/src/types/input_file.rs +++ b/src/types/input_file.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use std::path::PathBuf; +use std::{borrow::Cow, path::PathBuf}; /// This object represents the contents of a file to be uploaded. /// @@ -8,6 +8,7 @@ use std::path::PathBuf; #[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize)] pub enum InputFile { File(PathBuf), + Memory { file_name: String, data: Cow<'static, [u8]> }, Url(String), FileId(String), } @@ -20,6 +21,14 @@ impl InputFile { Self::File(path.into()) } + pub fn memory<S, D>(file_name: S, data: D) -> Self + where + S: Into<String>, + D: Into<Cow<'static, [u8]>>, + { + Self::Memory { file_name: file_name.into(), data: data.into() } + } + pub fn url<T>(url: T) -> Self where T: Into<String>, @@ -82,6 +91,14 @@ impl Serialize for InputFile { ), ) } + InputFile::Memory { data, .. } => { + // NOTE: file should be actually attached with + // multipart/form-data + serializer.serialize_str(&format!( + "attach://{}", + String::from_utf8_lossy(data) + )) + } InputFile::Url(url) => serializer.serialize_str(url), InputFile::FileId(id) => serializer.serialize_str(id), }