Refactor multipart requests

This removes the logic in the multipart serializer that unserialized
`InputFile`s from serde. Now `InputFile`s  are serialized either as
their value (for `FileId` and `Url`) or as an `attach://<id>` string
where `<id>` is replaced with some id unique for the file. The file data
itself is acquired through `MultipartPayload` trait.

Since the `<id>` must be the same while serializing the file with serde
and while acquiring data through `MultipartPayload` trait, `InputFile`
needs to store said id. As such, `InputFile` is now a structure with
private fields and it's structure can't be observed by users. The only
things that `InputFile` provides are
- Constructors (`url`, `file_id`, `file`, `memory`)
- File name setter
- `Clone` and `Debug` implementations
This commit is contained in:
Maybe Waffle 2022-01-13 15:11:50 +03:00
parent bed5805610
commit a84e897db9
12 changed files with 652 additions and 743 deletions

View file

@ -39,6 +39,7 @@ derive_more = "0.99.9"
mime = "0.3.16"
thiserror = "1.0.20"
once_cell = "1.5.0"
take_mut = "0.2"
never = "0.1.0"
chrono = { version = "0.4.19", default-features = false }
either = "1.6.1"

View file

@ -233,7 +233,7 @@ impl Bot {
pub(crate) fn execute_multipart<P>(
&self,
payload: &P,
payload: &mut P,
) -> impl Future<Output = ResponseResult<P::Output>>
where
P: MultipartPayload + Serialize,
@ -247,7 +247,35 @@ impl Bot {
// async move to capture client&token&api_url&params
async move {
let params = params.await?;
let params = params?.await;
net::request_multipart(
&client,
token.as_ref(),
reqwest::Url::clone(&*api_url),
P::NAME,
params,
)
.await
}
}
pub(crate) fn execute_multipart_ref<P>(
&self,
payload: &P,
) -> impl Future<Output = ResponseResult<P::Output>>
where
P: MultipartPayload + Serialize,
P::Output: DeserializeOwned,
{
let client = self.client.clone();
let token = Arc::clone(&self.token);
let api_url = self.api_url.clone();
let params = serde_multipart::to_form_ref(payload);
// async move to capture client&token&api_url&params
async move {
let params = params?.await;
net::request_multipart(
&client,
token.as_ref(),

View file

@ -1,41 +1,3 @@
macro_rules! forward_to_unsuported_ty {
(
supported: $supported:expr;
simple { $( $method:ident $arg:ty )* }
unit { $( $method1:ident $ty:expr )* }
compound {
$( $method2:ident $( <$T:ident: ?Sized + Serialize> )? ( $( $args:tt )* ) -> $ret:ty => $message:expr )*
}
) => {
$(
fn $method(self, _: $arg) -> Result<Self::Ok, Self::Error> {
Err(Self::Error::UnsupportedType {
ty: stringify!($arg),
supported: $supported,
})
}
)+
$(
fn $method1(self) -> Result<Self::Ok, Self::Error> {
Err(Self::Error::UnsupportedType {
ty: $ty,
supported: $supported,
})
}
)+
$(
fn $method2 $( <$T: ?Sized + Serialize> )? (self, $( $args )*) -> Result<$ret, Self::Error> {
Err(Self::Error::UnsupportedType {
ty: $message,
supported: $supported,
})
}
)+
};
}
macro_rules! req_future {
(
$v2:vis def: | $( $arg:ident: $ArgTy:ty ),* $(,)? | $body:block
@ -388,7 +350,19 @@ macro_rules! impl_payload {
$e
};
(@[multipart = $($multipart_attr:ident),*] $Method:ident req { $($reqf:ident),* } opt { $($optf:ident),*} ) => {
impl crate::requests::MultipartPayload for $Method {}
impl crate::requests::MultipartPayload for $Method {
fn copy_files(&self, into: &mut dyn FnMut(crate::types::InputFile)) {
$(
crate::types::InputFileLike::copy_into(&self.$multipart_attr, into);
)*
}
fn move_files(&mut self, into: &mut dyn FnMut(crate::types::InputFile)) {
$(
crate::types::InputFileLike::move_into(&mut self.$multipart_attr, into);
)*
}
}
};
(@[] $($ignored:tt)*) => {}
}

View file

@ -90,7 +90,7 @@ where
req_future! {
def: |it: MultipartRequest<U>| {
it.bot.execute_multipart(&it.payload)
it.bot.execute_multipart(&mut {it.payload})
}
pub Send<U> (inner0) -> ResponseResult<U::Output>
where
@ -101,7 +101,7 @@ req_future! {
req_future! {
def: |it: &MultipartRequest<U>| {
it.bot.execute_multipart(&it.payload)
it.bot.execute_multipart_ref(&it.payload)
}
pub SendRef<U> (inner1) -> ResponseResult<U::Output>
where

View file

@ -1,11 +1,49 @@
use crate::{payloads, requests::Payload};
use crate::{
payloads,
requests::Payload,
types::{InputFile, InputFileLike, InputMedia},
};
/// Payloads that need to be sent as `multipart/form-data` because they contain
/// files inside.
pub trait MultipartPayload: Payload {}
pub trait MultipartPayload: Payload {
fn copy_files(&self, into: &mut dyn FnMut(InputFile));
impl MultipartPayload for payloads::SendMediaGroup {}
fn move_files(&mut self, into: &mut dyn FnMut(InputFile));
}
impl MultipartPayload for payloads::EditMessageMedia {}
impl MultipartPayload for payloads::SendMediaGroup {
fn copy_files(&self, into: &mut dyn FnMut(InputFile)) {
self.media
.iter()
.flat_map(InputMedia::files)
.for_each(|f| f.copy_into(into))
}
impl MultipartPayload for payloads::EditMessageMediaInline {}
fn move_files(&mut self, into: &mut dyn FnMut(InputFile)) {
self.media
.iter_mut()
.flat_map(InputMedia::files_mut)
.for_each(|f| f.move_into(into))
}
}
impl MultipartPayload for payloads::EditMessageMedia {
fn copy_files(&self, into: &mut dyn FnMut(InputFile)) {
self.media.files().for_each(|f| f.copy_into(into))
}
fn move_files(&mut self, into: &mut dyn FnMut(InputFile)) {
self.media.files_mut().for_each(|f| f.move_into(into))
}
}
impl MultipartPayload for payloads::EditMessageMediaInline {
fn copy_files(&self, into: &mut dyn FnMut(InputFile)) {
self.media.files().for_each(|f| f.copy_into(into))
}
fn move_files(&mut self, into: &mut dyn FnMut(InputFile)) {
self.media.files_mut().for_each(|f| f.move_into(into))
}
}

View file

@ -0,0 +1,53 @@
use std::fmt;
use serde::ser;
use crate::RequestError;
#[derive(Debug, derive_more::From)]
pub(crate) enum Error {
Custom(String),
TopLevelNotStruct,
Fmt(std::fmt::Error),
Io(std::io::Error),
Json(serde_json::Error),
}
impl ser::Error for Error {
fn custom<T>(msg: T) -> Self
where
T: fmt::Display,
{
Self::Custom(msg.to_string())
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Custom(s) => write!(f, "Custom serde error: {}", s),
Self::TopLevelNotStruct => write!(f, "Multipart supports only structs at top level"),
Self::Fmt(inner) => write!(f, "Formatting error: {}", inner),
Self::Io(inner) => write!(f, "Io error: {}", inner),
Self::Json(inner) => write!(f, "Json (de)serialization error: {}", inner),
}
}
}
impl std::error::Error for Error {}
impl From<Error> for RequestError {
fn from(err: Error) -> Self {
match err {
Error::Io(ioerr) => RequestError::Io(ioerr),
// This should be ok since we (hopefuly) don't write request those may trigger errors
// and `Error` is internal.
e => unreachable!(
"we don't create requests those fail to serialize (if you see this, open an issue \
:|): {}",
e
),
}
}
}

View file

@ -10,24 +10,74 @@
//! This whole module is an awful hack and we'll probably stop using it in next
//! versions (in favor of something less automatic, but more simple).
mod error;
mod serializers;
mod unserializers;
use std::future::Future;
use reqwest::multipart::Form;
use serde::Serialize;
use serializers::MultipartTopLvlSerializer;
use crate::requests::MultipartPayload;
use error::Error;
use serializers::MultipartSerializer;
pub(crate) use serializers::Error;
/// Serializes given value into [`Form`]
/// Serializes given value into [`Form`] **taking all input files out**.
///
/// [`Form`]: reqwest::multipart::Form
pub(crate) fn to_form<T: ?Sized + Serialize>(val: &T) -> impl Future<Output = Result<Form, Error>> {
let fut = val.serialize(MultipartTopLvlSerializer {});
async { Ok(fut?.await?) }
pub(crate) fn to_form<T>(val: &mut T) -> Result<impl Future<Output = Form>, Error>
where
T: Serialize + MultipartPayload,
{
let mut form = val.serialize(MultipartSerializer::new())?;
let mut vec = Vec::with_capacity(1);
val.move_files(&mut |f| vec.push(f));
let iter = vec.into_iter();
let fut = async move {
for file in iter {
if file.needs_attach() {
let id = file.id().to_owned();
if let Some(part) = file.into_part() {
form = form.part(id, part.await);
}
}
}
form
};
Ok(fut)
}
/// Serializes given value into [`Form`].
///
/// [`Form`]: reqwest::multipart::Form
pub(crate) fn to_form_ref<T: ?Sized>(val: &T) -> Result<impl Future<Output = Form>, Error>
where
T: Serialize + MultipartPayload,
{
let mut form = val.serialize(MultipartSerializer::new())?;
let mut vec = Vec::with_capacity(1);
val.copy_files(&mut |f| vec.push(f));
let iter = vec.into_iter();
let fut = async move {
for file in iter {
if file.needs_attach() {
let id = file.id().to_owned();
if let Some(part) = file.into_part() {
form = form.part(id, part.await);
}
}
}
form
};
Ok(fut)
}
// https://github.com/teloxide/teloxide/issues/473
@ -39,13 +89,13 @@ async fn issue_473() {
types::{InputFile, MessageEntity, MessageEntityKind},
};
to_form(
to_form_ref(
&payloads::SendPhoto::new(0, InputFile::file_id("0")).caption_entities([MessageEntity {
kind: MessageEntityKind::Url,
offset: 0,
length: 0,
}]),
)
.await
.unwrap();
.unwrap()
.await;
}

View file

@ -1,87 +1,84 @@
use crate::{
serde_multipart::unserializers::{InputFileUnserializer, StringUnserializer},
types::InputFile,
RequestError,
};
use futures::{
future::{ready, BoxFuture},
stream::FuturesUnordered,
FutureExt, StreamExt, TryStreamExt,
};
use crate::serde_multipart::error::Error;
use reqwest::multipart::{Form, Part};
use serde::{
ser,
ser::{Impossible, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant},
ser::{Impossible, SerializeMap, SerializeSeq, SerializeStruct},
Serialize, Serializer,
};
use std::{fmt, fmt::Display, io};
#[derive(Debug, derive_more::From)]
pub(crate) enum Error {
Custom(String),
TopLevelNotStruct,
InputFileUnserializer(crate::serde_multipart::unserializers::UnserializerError),
Io(std::io::Error),
Json(serde_json::Error),
/// The main serializer that serializes top-level and structures
pub(super) struct MultipartSerializer(Form);
/// Serializer for maps (support for `#[serde(flatten)]`)
pub(super) struct MultipartMapSerializer {
form: Form,
key: Option<String>,
}
impl ser::Error for Error {
fn custom<T>(msg: T) -> Self
where
T: Display,
{
Self::Custom(msg.to_string())
/// Serializer for single "fields" that are serialized as multipart "part"s.
///
/// - Integers serialized as their text decimal representation
/// - Strings and byte slices are serialized as-is, without any changes
/// - Structs are serialized with JSON
/// - C-like enums are serialized as their names
struct PartSerializer;
/// Struct or Seq -> Json -> Part serializer
struct JsonPartSerializer {
buf: String,
state: PartSerializerStructState,
}
/// State for `PartSerializerStruct`
///
/// Json doesn't allow trailing commas, so we need to know if we already
/// serialized something and need to add a comma before next field
enum PartSerializerStructState {
Empty,
Rest,
}
impl MultipartSerializer {
pub(super) fn new() -> Self {
Self(Form::new())
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Custom(s) => write!(f, "Custom serde error: {}", s),
Self::TopLevelNotStruct => write!(f, "Multipart supports only structs at top level"),
Self::InputFileUnserializer(inner) => {
write!(f, "Error while unserializing input file: {}", inner)
}
Self::Io(inner) => write!(f, "Io error: {}", inner),
Self::Json(inner) => write!(f, "Json (de)serialization error: {}", inner),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
impl From<Error> for RequestError {
fn from(err: Error) -> Self {
match err {
Error::Io(ioerr) => RequestError::Io(ioerr),
// this should be ok since we don't write request those may trigger errors and
// Error is internal.
e => unreachable!(
"we don't create requests those fail to serialize (if you see this, open an issue \
:|): {}",
e
),
}
}
}
pub(crate) struct MultipartTopLvlSerializer {}
impl Serializer for MultipartTopLvlSerializer {
type Ok = <MultipartSerializer as SerializeStruct>::Ok;
impl Serializer for MultipartSerializer {
type Ok = Form;
type Error = Error;
// for `serde(flatten)` (e.g.: in CreateNewStickerSet)
type SerializeMap = MultipartMapSerializer;
// The main serializer - struct
type SerializeStruct = Self;
// Unimplemented
type SerializeSeq = Impossible<Self::Ok, Self::Error>;
type SerializeTuple = Impossible<Self::Ok, Self::Error>;
type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
type SerializeMap = MultipartMapSerializer; // for `serde(flatten)` (e.g.: in CreateNewStickerSet)
type SerializeStruct = MultipartSerializer;
type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Ok(MultipartMapSerializer {
form: Form::new(),
key: None,
})
}
fn serialize_struct(
self,
_: &'static str,
_: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Ok(self)
}
// Everything down below in this impl just returns
// `Err(Error::TopLevelNotStruct)`
fn serialize_bool(self, _: bool) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
@ -216,22 +213,6 @@ impl Serializer for MultipartTopLvlSerializer {
Err(Error::TopLevelNotStruct)
}
fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Ok(MultipartMapSerializer {
parts: vec![],
files: vec![],
key: None,
})
}
fn serialize_struct(
self,
_: &'static str,
_: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Ok(MultipartSerializer::new())
}
fn serialize_struct_variant(
self,
_: &'static str,
@ -243,22 +224,8 @@ impl Serializer for MultipartTopLvlSerializer {
}
}
pub(crate) struct MultipartSerializer {
parts: Vec<(&'static str, Part)>, // TODO: Array vecs
files: Vec<(String, InputFile)>,
}
impl MultipartSerializer {
fn new() -> Self {
Self {
parts: Vec::new(),
files: vec![],
}
}
}
impl SerializeStruct for MultipartSerializer {
type Ok = BoxFuture<'static, io::Result<Form>>; // impl Future<Output = io::Result<Form>>
type Ok = Form;
type Error = Error;
fn serialize_field<T: ?Sized>(
@ -269,52 +236,29 @@ impl SerializeStruct for MultipartSerializer {
where
T: Serialize,
{
let (part, file) = value.serialize(PartSerializer {})?;
self.parts.push((key, part));
self.files.extend(file);
let part = value.serialize(PartSerializer {})?;
take_mut::take(&mut self.0, |f| f.part(key, part));
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
let form = self
.parts
.into_iter()
.fold(Form::new(), |acc, (key, value)| acc.part(key, value));
if self.files.is_empty() {
//Ok(Either::Left(ready(Ok(form))))
Ok(Box::pin(ready(Ok(form))))
} else {
let fut = self
.files
.into_iter()
.map(|(k, f)| f.into_part().map(move |p| (k, p)))
.collect::<FuturesUnordered<_>>()
.map(Ok)
.try_fold(form, |acc, (k, p)| async { Ok(acc.part(k, p?)) });
//Ok(Either::Right(fut))
Ok(Box::pin(fut))
}
Ok(self.0)
}
}
pub(crate) struct MultipartMapSerializer {
parts: Vec<(String, Part)>, // TODO: Array vecs
files: Vec<(String, InputFile)>,
key: Option<String>,
}
impl SerializeMap for MultipartMapSerializer {
type Ok = <MultipartSerializer as SerializeStruct>::Ok;
type Ok = Form;
type Error = Error;
fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
self.key = Some(key.serialize(StringUnserializer)?);
if let Ok(serde_json::Value::String(s)) = serde_json::to_value(key) {
self.key = Some(s);
}
Ok(())
}
@ -322,110 +266,90 @@ impl SerializeMap for MultipartMapSerializer {
where
T: Serialize,
{
let key = self.key.take().unwrap();
let key = self
.key
.take()
.expect("Value serialized before key or key is not string");
let (part, file) = value.serialize(PartSerializer {})?;
self.parts.push((key, part));
self.files.extend(file);
let part = value.serialize(PartSerializer {})?;
take_mut::take(&mut self.form, |f| f.part(key, part));
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
let form = self
.parts
.into_iter()
.fold(Form::new(), |acc, (key, value)| acc.part(key, value));
if self.files.is_empty() {
//Ok(Either::Left(ready(Ok(form))))
Ok(Box::pin(ready(Ok(form))))
} else {
let fut = self
.files
.into_iter()
.map(|(k, f)| f.into_part().map(move |p| (k, p)))
.collect::<FuturesUnordered<_>>()
.map(Ok)
.try_fold(form, |acc, (k, p)| async { Ok(acc.part(k, p?)) });
//Ok(Either::Right(fut))
Ok(Box::pin(fut))
}
Ok(self.form)
}
}
struct PartSerializer {}
impl Serializer for PartSerializer {
type Ok = (Part, Vec<(String, InputFile)>);
type Ok = Part;
type Error = Error;
type SerializeSeq = InnerPartSerializer;
type SerializeStruct = JsonPartSerializer;
type SerializeSeq = JsonPartSerializer;
// Unimplemented
type SerializeTuple = Impossible<Self::Ok, Self::Error>;
type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
type SerializeMap = Impossible<Self::Ok, Self::Error>;
type SerializeStruct = PartSerializerStruct;
type SerializeStructVariant = PartFromFile;
type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
Ok((Part::text(v.to_string()), Vec::new()))
Ok(Part::text(v.to_string()))
}
fn serialize_i8(self, _: i8) -> Result<Self::Ok, Self::Error> {
unimplemented!()
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
Ok(Part::text(v.to_string()))
}
fn serialize_i16(self, _: i16) -> Result<Self::Ok, Self::Error> {
unimplemented!()
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
Ok(Part::text(v.to_string()))
}
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
Ok((Part::text(v.to_string()), Vec::new()))
Ok(Part::text(v.to_string()))
}
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
Ok((Part::text(v.to_string()), Vec::new()))
Ok(Part::text(v.to_string()))
}
fn serialize_u8(self, _: u8) -> Result<Self::Ok, Self::Error> {
unimplemented!()
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
Ok(Part::text(v.to_string()))
}
fn serialize_u16(self, _: u16) -> Result<Self::Ok, Self::Error> {
unimplemented!()
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
Ok(Part::text(v.to_string()))
}
fn serialize_u32(self, _: u32) -> Result<Self::Ok, Self::Error> {
unimplemented!()
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
Ok(Part::text(v.to_string()))
}
fn serialize_u64(self, _: u64) -> Result<Self::Ok, Self::Error> {
unimplemented!()
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
Ok(Part::text(v.to_string()))
}
fn serialize_f32(self, _: f32) -> Result<Self::Ok, Self::Error> {
unimplemented!()
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
Ok(Part::text(v.to_string()))
}
fn serialize_f64(self, _: f64) -> Result<Self::Ok, Self::Error> {
unimplemented!()
fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
Ok(Part::text(v.to_string()))
}
fn serialize_char(self, _: char) -> Result<Self::Ok, Self::Error> {
unimplemented!()
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
Ok(Part::text(v.to_string()))
}
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
Ok((Part::text(v.to_owned()), Vec::new()))
Ok(Part::text(v.to_owned()))
}
fn serialize_bytes(self, _: &[u8]) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
unimplemented!()
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
Ok(Part::bytes(v.to_owned()))
}
fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
@ -435,21 +359,48 @@ impl Serializer for PartSerializer {
value.serialize(self)
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_unit_struct(self, _: &'static str) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_unit_variant(
self,
_: &'static str,
_: u32,
variant_name: &'static str,
) -> Result<Self::Ok, Self::Error> {
Ok((Part::text(variant_name), Vec::new()))
Ok(Part::text(variant_name))
}
fn serialize_struct(
self,
_: &'static str,
_: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Ok(JsonPartSerializer {
buf: String::new(),
state: PartSerializerStructState::Empty,
})
}
fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Ok(JsonPartSerializer {
buf: String::new(),
state: PartSerializerStructState::Empty,
})
}
// Unimplemented
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
unimplemented!(
"We use `#[serde_with_macros::skip_serializing_none]` everywhere so `None`s are not \
serialized"
)
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_unit_struct(self, _: &'static str) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_newtype_struct<T: ?Sized>(
@ -465,37 +416,15 @@ impl Serializer for PartSerializer {
fn serialize_newtype_variant<T: ?Sized>(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
value: &T,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: Serialize,
{
let file: InputFile = InputFileUnserializer::NotMem.serialize_newtype_variant(
name,
variant_index,
variant,
value,
)?;
match file {
f @ InputFile::Memory { .. } | f @ InputFile::File(_) => {
let uuid = uuid::Uuid::new_v4().to_string();
let part = Part::text(format!("attach://{}", uuid));
Ok((part, vec![(uuid, f)]))
}
InputFile::FileId(s) => Ok((Part::text(s), Vec::new())),
InputFile::Url(s) => Ok((Part::text(String::from(s)), Vec::new())),
}
}
fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Ok(Self::SerializeSeq {
array_json_parts: vec![],
files: vec![],
})
unimplemented!()
}
fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple, Self::Error> {
@ -524,44 +453,19 @@ impl Serializer for PartSerializer {
unimplemented!()
}
fn serialize_struct(
self,
name: &'static str,
len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
let mut ser = serde_json::Serializer::new(Vec::new());
ser.serialize_struct(name, len)?;
Ok(PartSerializerStruct(
ser, // TODO: capcity
serde_json::ser::State::First,
Vec::new(),
))
}
fn serialize_struct_variant(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
len: usize,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
Ok(PartFromFile {
inner: InputFileUnserializer::memory().serialize_struct_variant(
name,
variant_index,
variant,
len,
)?,
})
unimplemented!()
}
}
struct PartFromFile {
inner: InputFileUnserializer,
}
impl SerializeStructVariant for PartFromFile {
type Ok = (Part, Vec<(String, InputFile)>);
impl SerializeStruct for JsonPartSerializer {
type Ok = Part;
type Error = Error;
fn serialize_field<T: ?Sized>(
@ -572,158 +476,71 @@ impl SerializeStructVariant for PartFromFile {
where
T: Serialize,
{
self.inner
.serialize_field(key, value)
.map_err(Error::InputFileUnserializer)
}
use std::fmt::Write;
use PartSerializerStructState::*;
fn end(self) -> Result<Self::Ok, Self::Error> {
let file = self.inner.end()?;
let value = serde_json::to_string(value)?;
match self.state {
Empty => {
self.state = Rest;
// TODO: to method
match file {
f @ InputFile::Memory { .. } | f @ InputFile::File(_) => {
let uuid = uuid::Uuid::new_v4().to_string();
let part = Part::text(format!("attach://{}", uuid));
Ok((part, vec![(uuid, f)]))
write!(&mut self.buf, "{{\"{}\":{}", key, value)?
}
InputFile::FileId(s) => Ok((Part::text(s), vec![])),
InputFile::Url(s) => Ok((Part::text(String::from(s)), vec![])),
}
}
}
struct InnerPartSerializer {
array_json_parts: Vec<serde_json::Value>, // using value is such a workaround :|
files: Vec<(String, InputFile)>,
}
impl SerializeSeq for InnerPartSerializer {
type Ok = (Part, Vec<(String, InputFile)>);
type Error = Error;
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
// NOTE: this is probably highly inefficient (especially for ::Memory),
// but at least it works
let mut value = serde_json::to_value(value)?;
// Update value if it contains InputFile in a `media` field (i.e.: `InputMedia`)
if let Some(file) = value.get_mut("media") {
let file: InputFile = serde_json::from_value(file.take())?;
match file {
f @ InputFile::Memory { .. } | f @ InputFile::File(_) => {
let uuid = uuid::Uuid::new_v4().to_string();
value["media"] = serde_json::Value::String(format!("attach://{}", uuid));
self.files.push((uuid, f));
}
InputFile::FileId(s) => {
value["media"] = serde_json::Value::String(s);
}
InputFile::Url(s) => {
value["media"] = serde_json::Value::String(String::from(s));
}
}
}
self.array_json_parts.push(value);
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
let s = serde_json::to_string(&self.array_json_parts)?;
Ok((Part::text(s), self.files))
}
}
struct PartSerializerStruct(
serde_json::Serializer<Vec<u8>>,
serde_json::ser::State,
Vec<(String, InputFile)>,
);
impl SerializeStruct for PartSerializerStruct {
type Ok = (Part, Vec<(String, InputFile)>);
type Error = Error;
fn serialize_field<T: ?Sized>(
&mut self,
key: &'static str,
value: &T,
) -> Result<(), Self::Error>
where
T: Serialize,
{
let state = match self.1 {
serde_json::ser::State::Empty => serde_json::ser::State::Empty,
serde_json::ser::State::First => serde_json::ser::State::First,
serde_json::ser::State::Rest => serde_json::ser::State::Rest,
};
let mut ser = serde_json::ser::Compound::Map {
ser: &mut self.0,
state,
};
// special case media (required for `edit_message_media` to work)
if key == "media" {
let file = value.serialize(InputFileUnserializer::NotMem)?;
match file {
f @ InputFile::Memory { .. } | f @ InputFile::File(_) => {
let uuid = uuid::Uuid::new_v4().to_string();
let attach = format!("attach://{}", uuid);
SerializeStruct::serialize_field(&mut ser, key, attach.as_str())?;
self.1 = get_state(ser);
self.2.push((uuid, f));
}
InputFile::FileId(s) => {
SerializeStruct::serialize_field(&mut ser, key, &s)?;
self.1 = get_state(ser)
}
InputFile::Url(s) => {
SerializeStruct::serialize_field(&mut ser, key, s.as_str())?;
self.1 = get_state(ser)
}
}
} else {
SerializeStruct::serialize_field(&mut ser, key, value)?;
self.1 = get_state(ser);
Rest => write!(&mut self.buf, ",\"{}\":{}", key, value)?,
}
Ok(())
}
fn end(mut self) -> Result<Self::Ok, Self::Error> {
let state = match self.1 {
serde_json::ser::State::Empty => serde_json::ser::State::Empty,
serde_json::ser::State::First => serde_json::ser::State::First,
serde_json::ser::State::Rest => serde_json::ser::State::Rest,
};
let ser = serde_json::ser::Compound::Map {
ser: &mut self.0,
state,
};
SerializeStruct::end(ser)?;
use PartSerializerStructState::*;
let json = self.0.into_inner();
Ok((Part::bytes(json), self.2))
match self.state {
Empty => Ok(Part::text("{{}}")),
Rest => {
self.buf += "}";
Ok(Part::text(self.buf))
}
}
}
}
fn get_state(
compound: serde_json::ser::Compound<Vec<u8>, serde_json::ser::CompactFormatter>,
) -> serde_json::ser::State {
// Compound may have more variants under some serde_json features
#[allow(unreachable_patterns)]
match compound {
serde_json::ser::Compound::Map { ser: _, state } => state,
_ => unreachable!(),
impl SerializeSeq for JsonPartSerializer {
type Ok = Part;
type Error = Error;
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
use std::fmt::Write;
use PartSerializerStructState::*;
let value = serde_json::to_string(value)?;
match self.state {
Empty => {
self.state = Rest;
write!(&mut self.buf, "[{}", value)?
}
Rest => write!(&mut self.buf, ",{}", value)?,
}
Ok(())
}
fn end(mut self) -> Result<Self::Ok, Self::Error> {
use PartSerializerStructState::*;
match self.state {
Empty => Ok(Part::text("[]")),
Rest => {
self.buf += "]";
Ok(Part::text(self.buf))
}
}
}
}

View file

@ -1,145 +0,0 @@
use crate::serde_multipart::unserializers::UnserializerError;
use serde::{
ser::{Impossible, SerializeSeq},
Serialize, Serializer,
};
#[derive(Default)]
pub(crate) struct BytesUnserializer(Vec<u8>);
impl Serializer for BytesUnserializer {
type Ok = Vec<u8>;
type Error = UnserializerError;
type SerializeSeq = Self;
type SerializeTuple = Impossible<Vec<u8>, UnserializerError>;
type SerializeTupleStruct = Impossible<Vec<u8>, UnserializerError>;
type SerializeTupleVariant = Impossible<Vec<u8>, UnserializerError>;
type SerializeMap = Impossible<Vec<u8>, UnserializerError>;
type SerializeStruct = Impossible<Vec<u8>, UnserializerError>;
type SerializeStructVariant = Impossible<Vec<u8>, UnserializerError>;
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
Ok(v.to_owned())
}
fn serialize_seq(mut self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
if let Some(len) = len {
self.0.reserve_exact(len);
}
Ok(self)
}
forward_to_unsuported_ty! {
supported: "&[u8], Vec<u8>, Cow<[u8]>";
simple {
serialize_bool bool
serialize_i8 i8
serialize_i16 i16
serialize_i32 i32
serialize_i64 i64
serialize_u8 u8
serialize_u16 u16
serialize_u32 u32
serialize_u64 u64
serialize_f32 f32
serialize_f64 f64
serialize_char char
serialize_str &str
}
unit {
serialize_none "None"
serialize_unit "unit"
}
compound {
serialize_some<T: ?Sized + Serialize>(_: &T) -> Self::Ok => "Some(_)"
serialize_unit_struct(_: &'static str) -> Self::Ok => "unit struct"
serialize_unit_variant(_: &'static str, _: u32, _: &'static str) -> Self::Ok => "unit variant"
serialize_newtype_struct<T: ?Sized + Serialize>(_: &'static str, _: &T) -> Self::Ok => "newtype struct"
serialize_newtype_variant<T: ?Sized + Serialize>(_: &'static str, _: u32, _: &'static str, _: &T) -> Self::Ok => "newtype variant"
serialize_tuple(_: usize) -> Self::SerializeTuple => "tuple"
serialize_tuple_struct(_: &'static str, _: usize) -> Self::SerializeTupleStruct => "tuple struct"
serialize_tuple_variant(_: &'static str, _: u32, _: &'static str, _: usize) -> Self::SerializeTupleVariant => "tuple variant"
serialize_map(_: Option<usize>) -> Self::SerializeMap => "map"
serialize_struct(_: &'static str, _: usize) -> Self::SerializeStruct => "struct"
serialize_struct_variant(_: &'static str, _: u32, _: &'static str, _: usize) -> Self::SerializeStructVariant => "struct variant"
}
}
}
impl SerializeSeq for BytesUnserializer {
type Ok = Vec<u8>;
type Error = UnserializerError;
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
value.serialize(BytesUnserializerPush(&mut self.0))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(self.0)
}
}
pub(crate) struct BytesUnserializerPush<'a>(&'a mut Vec<u8>);
impl Serializer for BytesUnserializerPush<'_> {
type Ok = ();
type Error = UnserializerError;
type SerializeSeq = Impossible<(), UnserializerError>;
type SerializeTuple = Impossible<(), UnserializerError>;
type SerializeTupleStruct = Impossible<(), UnserializerError>;
type SerializeTupleVariant = Impossible<(), UnserializerError>;
type SerializeMap = Impossible<(), UnserializerError>;
type SerializeStruct = Impossible<(), UnserializerError>;
type SerializeStructVariant = Impossible<(), UnserializerError>;
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
self.0.push(v);
Ok(())
}
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
self.0.extend_from_slice(v);
Ok(())
}
forward_to_unsuported_ty! {
supported: "&[u8], Vec<u8>, Cow<[u8]>";
simple {
serialize_bool bool
serialize_i8 i8
serialize_i16 i16
serialize_i32 i32
serialize_i64 i64
serialize_u16 u16
serialize_u32 u32
serialize_u64 u64
serialize_f32 f32
serialize_f64 f64
serialize_char char
serialize_str &str
}
unit {
serialize_none "None"
serialize_unit "unit"
}
compound {
serialize_some<T: ?Sized + Serialize>(_: &T) -> Self::Ok => "Some(_)"
serialize_unit_struct(_: &'static str) -> Self::Ok => "unit struct"
serialize_unit_variant(_: &'static str, _: u32, _: &'static str) -> Self::Ok => "unit variant"
serialize_newtype_struct<T: ?Sized + Serialize>(_: &'static str, _: &T) -> Self::Ok => "newtype struct"
serialize_newtype_variant<T: ?Sized + Serialize>(_: &'static str, _: u32, _: &'static str, _: &T) -> Self::Ok => "newtype variant"
serialize_seq(_: Option<usize>) -> Self::SerializeSeq => "sequence"
serialize_tuple(_: usize) -> Self::SerializeTuple => "tuple"
serialize_tuple_struct(_: &'static str, _: usize) -> Self::SerializeTupleStruct => "tuple struct"
serialize_tuple_variant(_: &'static str, _: u32, _: &'static str, _: usize) -> Self::SerializeTupleVariant => "tuple variant"
serialize_map(_: Option<usize>) -> Self::SerializeMap => "map"
serialize_struct(_: &'static str, _: usize) -> Self::SerializeStruct => "struct"
serialize_struct_variant(_: &'static str, _: u32, _: &'static str, _: usize) -> Self::SerializeStructVariant => "struct variant"
}
}
}

View file

@ -1,61 +0,0 @@
use crate::serde_multipart::unserializers::UnserializerError;
use serde::{ser::Impossible, Serialize, Serializer};
pub(crate) struct StringUnserializer;
impl Serializer for StringUnserializer {
type Ok = String;
type Error = UnserializerError;
type SerializeSeq = Impossible<String, UnserializerError>;
type SerializeTuple = Impossible<String, UnserializerError>;
type SerializeTupleStruct = Impossible<String, UnserializerError>;
type SerializeTupleVariant = Impossible<String, UnserializerError>;
type SerializeMap = Impossible<String, UnserializerError>;
type SerializeStruct = Impossible<String, UnserializerError>;
type SerializeStructVariant = Impossible<String, UnserializerError>;
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
Ok(v.to_string())
}
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
Ok(v.to_owned())
}
forward_to_unsuported_ty! {
supported: "&str, String";
simple {
serialize_bool bool
serialize_i8 i8
serialize_i16 i16
serialize_i32 i32
serialize_i64 i64
serialize_u8 u8
serialize_u16 u16
serialize_u32 u32
serialize_u64 u64
serialize_f32 f32
serialize_f64 f64
serialize_bytes &[u8]
}
unit {
serialize_none "None"
serialize_unit "unit"
}
compound {
serialize_some<T: ?Sized + Serialize>(_: &T) -> Self::Ok => "Some(_)"
serialize_unit_struct(_: &'static str) -> Self::Ok => "unit struct"
serialize_unit_variant(_: &'static str, _: u32, _: &'static str) -> Self::Ok => "unit variant"
serialize_newtype_struct<T: ?Sized + Serialize>(_: &'static str, _: &T) -> Self::Ok => "newtype struct"
serialize_newtype_variant<T: ?Sized + Serialize>(_: &'static str, _: u32, _: &'static str, _: &T) -> Self::Ok => "newtype variant"
serialize_seq(_: Option<usize>) -> Self::SerializeSeq => "sequence"
serialize_tuple(_: usize) -> Self::SerializeTuple => "tuple"
serialize_tuple_struct(_: &'static str, _: usize) -> Self::SerializeTupleStruct => "tuple struct"
serialize_tuple_variant(_: &'static str, _: u32, _: &'static str, _: usize) -> Self::SerializeTupleVariant => "tuple variant"
serialize_map(_: Option<usize>) -> Self::SerializeMap => "map"
serialize_struct(_: &'static str, _: usize) -> Self::SerializeStruct => "struct"
serialize_struct_variant(_: &'static str, _: u32, _: &'static str, _: usize) -> Self::SerializeStructVariant => "struct variant"
}
}
}

View file

@ -1,123 +1,277 @@
use reqwest::Url;
use serde::{Deserialize, Serialize};
use bytes::{Bytes, BytesMut};
use futures::{
future::{ready, Either},
stream,
};
use once_cell::sync::OnceCell;
use reqwest::{multipart::Part, Body};
use serde::Serialize;
use tokio_util::codec::{Decoder, FramedRead};
use std::{borrow::Cow, path::PathBuf};
use std::{borrow::Cow, fmt, future::Future, io, mem, path::PathBuf, sync::Arc};
use crate::types::InputSticker;
/// This object represents the contents of a file to be uploaded.
///
/// [The official docs](https://core.telegram.org/bots/api#inputfile).
#[derive(Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub enum InputFile {
#[derive(Debug, Clone)]
pub struct InputFile {
id: OnceCell<Arc<str>>,
file_name: Option<Cow<'static, str>>,
inner: InnerFile,
}
#[derive(Clone)]
enum InnerFile {
File(PathBuf),
Memory {
file_name: String,
data: Cow<'static, [u8]>,
},
Url(Url),
Bytes(bytes::Bytes),
Url(url::Url),
FileId(String),
}
use InnerFile::*;
impl InputFile {
pub fn file<P>(path: P) -> Self
where
P: Into<PathBuf>,
{
Self::File(path.into())
/// Creates an `InputFile` from an url.
///
/// Notes:
/// - When sending by URL the target file must have the correct MIME type
/// (e.g., `audio/mpeg` for [`SendAudio`], etc.).
/// - In [`SendDocument`], sending by URL will currently only work for
/// `GIF`, `PDF` and `ZIP` files.
/// - To use [`SendVoice`], the file must have the type audio/ogg and be no
/// more than 1MB in size. 1-20MB voice notes will be sent as files.
/// - Other configurations may work but we can't guarantee that they will.
///
/// [`SendAudio`]: crate::payloads::SendAudio
/// [`SendDocument`]: crate::payloads::SendDocument
/// [`SendVoice`]: crate::payloads::SendVoice
pub fn url(url: url::Url) -> Self {
Self::new(Url(url))
}
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(),
/// Creates an `InputFile` from a file id.
///
/// File id can be obtained from
///
/// Notes:
/// - It is not possible to change the file type when resending by file id.
/// I.e. a video can't be sent as a photo, a photo can't be sent as a
/// document, etc.
/// - It is not possible to resend thumbnails.
/// - Resending a photo by file id will send all of its [sizes].
/// - file id is unique for each individual bot and can't be transferred
/// from one bot to another.
/// - file id uniquely identifies a file, but a file can have different
/// valid file_ids even for the same bot.
///
/// [sizes]: crate::types::PhotoSize
pub fn file_id(file_id: impl Into<String>) -> Self {
Self::new(FileId(file_id.into()))
}
/// Creates an `InputFile` from a file path.
pub fn file(path: impl Into<PathBuf>) -> Self {
Self::new(File(path.into()))
}
/// Creates an `InputFile` from a in-memory bytes.
pub fn memory(data: impl Into<bytes::Bytes>) -> Self {
Self::new(Bytes(data.into()))
}
/// Set the file name for this file.
pub fn file_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
self.file_name = Some(name.into());
self
}
/// Shorthand for `Self { file_name: None, inner, id: default() }`
/// (private because `InnerFile` iы private implementation detail)
fn new(inner: InnerFile) -> Self {
Self {
file_name: None,
inner,
id: OnceCell::new(),
}
}
pub fn url(url: Url) -> Self {
Self::Url(url)
/// Returns id of this file.
///
/// This is used to coordinate with `attach://`.
pub(crate) fn id(&self) -> &str {
// FIXME: remove extra alloc
self.id
.get_or_init(|| uuid::Uuid::new_v4().to_string().into())
}
pub fn file_id<T>(file_id: T) -> Self
where
T: Into<String>,
{
Self::FileId(file_id.into())
/// Returns `true` if this file needs an attachment i.e. it's not a file_id
/// or url that can be serialized without any additional multipart parts.
pub(crate) fn needs_attach(&self) -> bool {
!matches!(self.inner, Url(_) | FileId(_))
}
pub fn as_file(&self) -> Option<&PathBuf> {
match self {
Self::File(path) => Some(path),
_ => None,
/// Takes this file out.
///
/// **Note**: this replaces `self` with a dummy value, this function should
/// only be used when the file is about to get dropped.
pub(crate) fn take(&mut self) -> Self {
mem::replace(self, InputFile::file_id(String::new()))
}
/// Returns an attach string for `multipart/form-data` in the form of
/// `"attach://{id}"` if this file should be uploaded via
/// `multipart/form-data`, or the value if it may be uploaded in any way (ie
/// it's an URL or file id).
fn attach_or_value(&self) -> String {
match &self.inner {
Url(url) => url.as_str().to_owned(),
FileId(file_id) => file_id.clone(),
_ => {
const PREFIX: &str = "attach://";
let id = self.id();
let mut s = String::with_capacity(PREFIX.len() + id.len());
s += PREFIX;
s += id;
s
}
}
}
pub fn as_url(&self) -> Option<&Url> {
match self {
Self::Url(url) => Some(url),
_ => None,
}
/// Takes the file name or tries to guess it based on file name in the path
/// if `File.0`. Returns an empty string if couldn't guess.
fn take_or_guess_filename(&mut self) -> Cow<'static, str> {
self.file_name.take().unwrap_or_else(|| match &self.inner {
File(path_to_file) => match path_to_file.file_name() {
Some(name) => Cow::Owned(name.to_string_lossy().into_owned()),
None => Cow::Borrowed(""),
},
_ => Cow::Borrowed(""),
})
}
}
pub fn as_file_id(&self) -> Option<&String> {
impl fmt::Debug for InnerFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::FileId(id) => Some(id),
_ => None,
File(path) => f.debug_struct("File").field("path", path).finish(),
Bytes(bytes) if f.alternate() => f.debug_tuple("Memory").field(bytes).finish(),
Bytes(_) => f.debug_struct("Memory").finish_non_exhaustive(),
Url(url) => f.debug_tuple("Url").field(url).finish(),
FileId(file_id) => f.debug_tuple("FileId").field(file_id).finish(),
}
}
}
impl From<InputFile> for Option<PathBuf> {
fn from(file: InputFile) -> Self {
match file {
InputFile::File(path) => Some(path),
_ => None,
}
impl Serialize for InputFile {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.attach_or_value().serialize(serializer)
}
}
// internal api
use reqwest::multipart::Part;
impl InputFile {
pub(crate) async fn into_part(self) -> std::io::Result<Part> {
use bytes::{Bytes, BytesMut};
use reqwest::Body;
use tokio_util::codec::{Decoder, FramedRead};
pub(crate) fn into_part(mut self) -> Option<impl Future<Output = Part>> {
let filename = self.take_or_guess_filename();
struct FileDecoder;
let file_part = match self.inner {
// Url and FileId are serialized just as strings, they don't need additional parts
Url(_) | FileId(_) => None,
impl Decoder for FileDecoder {
type Item = Bytes;
type Error = std::io::Error;
File(path_to_file) => {
let fut = async {
let body = match tokio::fs::File::open(path_to_file).await {
Ok(file) => {
let file = FramedRead::new(file, BytesDecoder);
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
if src.is_empty() {
return Ok(None);
}
Ok(Some(src.split().freeze()))
Body::wrap_stream(file)
}
Err(err) => {
// explicit type needed for `Bytes: From<?T>` in `wrap_stream`
let err = Err::<Bytes, _>(err);
Body::wrap_stream(stream::iter([err]))
}
};
Part::stream(body).file_name(filename)
};
Some(Either::Left(fut))
}
Bytes(data) => {
let stream = Part::stream(data).file_name(filename);
Some(Either::Right(ready(stream)))
}
};
file_part
}
}
struct BytesDecoder;
impl Decoder for BytesDecoder {
type Item = Bytes;
type Error = io::Error;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
if src.is_empty() {
return Ok(None);
}
Ok(Some(src.split().freeze()))
}
}
match self {
Self::File(path_to_file) => {
let file_name = path_to_file
.file_name()
.unwrap()
.to_string_lossy()
.into_owned();
/// An internal trait that is used in expansion of `impl_payload!` used to work
/// with input-file-like things (`InputFile` itself, `Option<InputFile>`,
/// `InputSticker`)
pub(crate) trait InputFileLike {
fn copy_into(&self, into: &mut dyn FnMut(InputFile));
let file = FramedRead::new(tokio::fs::File::open(path_to_file).await?, FileDecoder);
fn move_into(&mut self, into: &mut dyn FnMut(InputFile));
}
Ok(Part::stream(Body::wrap_stream(file)).file_name(file_name))
}
Self::Memory { file_name, data } => Ok(Part::bytes(data).file_name(file_name)),
Self::Url(s) => Ok(Part::text(String::from(s))),
Self::FileId(s) => Ok(Part::text(s)),
impl InputFileLike for InputFile {
fn copy_into(&self, into: &mut dyn FnMut(InputFile)) {
into(self.clone())
}
fn move_into(&mut self, into: &mut dyn FnMut(InputFile)) {
into(self.take())
}
}
impl InputFileLike for Option<InputFile> {
fn copy_into(&self, into: &mut dyn FnMut(InputFile)) {
if let Some(this) = self {
this.copy_into(into)
}
}
fn move_into(&mut self, into: &mut dyn FnMut(InputFile)) {
if let Some(this) = self {
this.move_into(into)
}
}
}
impl InputFileLike for InputSticker {
fn copy_into(&self, into: &mut dyn FnMut(InputFile)) {
let (InputSticker::Png(input_file) | InputSticker::Tgs(input_file)) = self;
input_file.copy_into(into)
}
fn move_into(&mut self, into: &mut dyn FnMut(InputFile)) {
let (InputSticker::Png(input_file) | InputSticker::Tgs(input_file)) = self;
input_file.move_into(into)
}
}

View file

@ -1,6 +1,6 @@
use std::iter;
use serde::{Deserialize, Serialize};
use serde::Serialize;
use crate::types::{InputFile, MessageEntity, ParseMode};
@ -533,7 +533,7 @@ mod tests {
#[test]
fn photo_serialize() {
let expected_json = r#"{"type":"photo","media":{"FileId":"123456"}}"#;
let expected_json = r#"{"type":"photo","media":"123456"}"#;
let photo = InputMedia::Photo(InputMediaPhoto {
media: InputFile::file_id("123456"),
caption: None,
@ -547,7 +547,7 @@ mod tests {
#[test]
fn video_serialize() {
let expected_json = r#"{"type":"video","media":{"FileId":"123456"}}"#;
let expected_json = r#"{"type":"video","media":"123456"}"#;
let video = InputMedia::Video(InputMediaVideo {
media: InputFile::file_id("123456"),
thumb: None,
@ -566,7 +566,7 @@ mod tests {
#[test]
fn animation_serialize() {
let expected_json = r#"{"type":"animation","media":{"FileId":"123456"}}"#;
let expected_json = r#"{"type":"animation","media":"123456"}"#;
let video = InputMedia::Animation(InputMediaAnimation {
media: InputFile::file_id("123456"),
thumb: None,
@ -584,7 +584,7 @@ mod tests {
#[test]
fn audio_serialize() {
let expected_json = r#"{"type":"audio","media":{"FileId":"123456"}}"#;
let expected_json = r#"{"type":"audio","media":"123456"}"#;
let video = InputMedia::Audio(InputMediaAudio {
media: InputFile::file_id("123456"),
thumb: None,
@ -602,7 +602,7 @@ mod tests {
#[test]
fn document_serialize() {
let expected_json = r#"{"type":"document","media":{"FileId":"123456"}}"#;
let expected_json = r#"{"type":"document","media":"123456"}"#;
let video = InputMedia::Document(InputMediaDocument {
media: InputFile::file_id("123456"),
thumb: None,