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" mime = "0.3.16"
thiserror = "1.0.20" thiserror = "1.0.20"
once_cell = "1.5.0" once_cell = "1.5.0"
take_mut = "0.2"
never = "0.1.0" never = "0.1.0"
chrono = { version = "0.4.19", default-features = false } chrono = { version = "0.4.19", default-features = false }
either = "1.6.1" either = "1.6.1"

View file

@ -233,7 +233,7 @@ impl Bot {
pub(crate) fn execute_multipart<P>( pub(crate) fn execute_multipart<P>(
&self, &self,
payload: &P, payload: &mut P,
) -> impl Future<Output = ResponseResult<P::Output>> ) -> impl Future<Output = ResponseResult<P::Output>>
where where
P: MultipartPayload + Serialize, P: MultipartPayload + Serialize,
@ -247,7 +247,35 @@ impl Bot {
// async move to capture client&token&api_url&params // async move to capture client&token&api_url&params
async move { 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( net::request_multipart(
&client, &client,
token.as_ref(), 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 { macro_rules! req_future {
( (
$v2:vis def: | $( $arg:ident: $ArgTy:ty ),* $(,)? | $body:block $v2:vis def: | $( $arg:ident: $ArgTy:ty ),* $(,)? | $body:block
@ -388,7 +350,19 @@ macro_rules! impl_payload {
$e $e
}; };
(@[multipart = $($multipart_attr:ident),*] $Method:ident req { $($reqf:ident),* } opt { $($optf:ident),*} ) => { (@[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)*) => {} (@[] $($ignored:tt)*) => {}
} }

View file

@ -90,7 +90,7 @@ where
req_future! { req_future! {
def: |it: MultipartRequest<U>| { def: |it: MultipartRequest<U>| {
it.bot.execute_multipart(&it.payload) it.bot.execute_multipart(&mut {it.payload})
} }
pub Send<U> (inner0) -> ResponseResult<U::Output> pub Send<U> (inner0) -> ResponseResult<U::Output>
where where
@ -101,7 +101,7 @@ req_future! {
req_future! { req_future! {
def: |it: &MultipartRequest<U>| { def: |it: &MultipartRequest<U>| {
it.bot.execute_multipart(&it.payload) it.bot.execute_multipart_ref(&it.payload)
} }
pub SendRef<U> (inner1) -> ResponseResult<U::Output> pub SendRef<U> (inner1) -> ResponseResult<U::Output>
where 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 /// Payloads that need to be sent as `multipart/form-data` because they contain
/// files inside. /// 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 //! 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). //! versions (in favor of something less automatic, but more simple).
mod error;
mod serializers; mod serializers;
mod unserializers;
use std::future::Future; use std::future::Future;
use reqwest::multipart::Form; use reqwest::multipart::Form;
use serde::Serialize; 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`] **taking all input files out**.
/// Serializes given value into [`Form`]
/// ///
/// [`Form`]: reqwest::multipart::Form /// [`Form`]: reqwest::multipart::Form
pub(crate) fn to_form<T: ?Sized + Serialize>(val: &T) -> impl Future<Output = Result<Form, Error>> { pub(crate) fn to_form<T>(val: &mut T) -> Result<impl Future<Output = Form>, Error>
let fut = val.serialize(MultipartTopLvlSerializer {}); where
async { Ok(fut?.await?) } 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 // https://github.com/teloxide/teloxide/issues/473
@ -39,13 +89,13 @@ async fn issue_473() {
types::{InputFile, MessageEntity, MessageEntityKind}, types::{InputFile, MessageEntity, MessageEntityKind},
}; };
to_form( to_form_ref(
&payloads::SendPhoto::new(0, InputFile::file_id("0")).caption_entities([MessageEntity { &payloads::SendPhoto::new(0, InputFile::file_id("0")).caption_entities([MessageEntity {
kind: MessageEntityKind::Url, kind: MessageEntityKind::Url,
offset: 0, offset: 0,
length: 0, length: 0,
}]), }]),
) )
.await .unwrap()
.unwrap(); .await;
} }

View file

@ -1,87 +1,84 @@
use crate::{ use crate::serde_multipart::error::Error;
serde_multipart::unserializers::{InputFileUnserializer, StringUnserializer},
types::InputFile,
RequestError,
};
use futures::{
future::{ready, BoxFuture},
stream::FuturesUnordered,
FutureExt, StreamExt, TryStreamExt,
};
use reqwest::multipart::{Form, Part}; use reqwest::multipart::{Form, Part};
use serde::{ use serde::{
ser, ser::{Impossible, SerializeMap, SerializeSeq, SerializeStruct},
ser::{Impossible, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant},
Serialize, Serializer, Serialize, Serializer,
}; };
use std::{fmt, fmt::Display, io};
#[derive(Debug, derive_more::From)] /// The main serializer that serializes top-level and structures
pub(crate) enum Error { pub(super) struct MultipartSerializer(Form);
Custom(String),
TopLevelNotStruct, /// Serializer for maps (support for `#[serde(flatten)]`)
InputFileUnserializer(crate::serde_multipart::unserializers::UnserializerError), pub(super) struct MultipartMapSerializer {
Io(std::io::Error), form: Form,
Json(serde_json::Error), key: Option<String>,
} }
impl ser::Error for Error { /// Serializer for single "fields" that are serialized as multipart "part"s.
fn custom<T>(msg: T) -> Self ///
where /// - Integers serialized as their text decimal representation
T: Display, /// - Strings and byte slices are serialized as-is, without any changes
{ /// - Structs are serialized with JSON
Self::Custom(msg.to_string()) /// - 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 { impl Serializer for MultipartSerializer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { type Ok = Form;
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;
type Error = Error; 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 SerializeSeq = Impossible<Self::Ok, Self::Error>;
type SerializeTuple = Impossible<Self::Ok, Self::Error>; type SerializeTuple = Impossible<Self::Ok, Self::Error>;
type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>; type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
type SerializeTupleVariant = 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>; 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> { fn serialize_bool(self, _: bool) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct) Err(Error::TopLevelNotStruct)
} }
@ -216,22 +213,6 @@ impl Serializer for MultipartTopLvlSerializer {
Err(Error::TopLevelNotStruct) 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( fn serialize_struct_variant(
self, self,
_: &'static str, _: &'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 { impl SerializeStruct for MultipartSerializer {
type Ok = BoxFuture<'static, io::Result<Form>>; // impl Future<Output = io::Result<Form>> type Ok = Form;
type Error = Error; type Error = Error;
fn serialize_field<T: ?Sized>( fn serialize_field<T: ?Sized>(
@ -269,52 +236,29 @@ impl SerializeStruct for MultipartSerializer {
where where
T: Serialize, T: Serialize,
{ {
let (part, file) = value.serialize(PartSerializer {})?; let part = value.serialize(PartSerializer {})?;
self.parts.push((key, part)); take_mut::take(&mut self.0, |f| f.part(key, part));
self.files.extend(file);
Ok(()) Ok(())
} }
fn end(self) -> Result<Self::Ok, Self::Error> { fn end(self) -> Result<Self::Ok, Self::Error> {
let form = self Ok(self.0)
.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))
}
} }
} }
pub(crate) struct MultipartMapSerializer {
parts: Vec<(String, Part)>, // TODO: Array vecs
files: Vec<(String, InputFile)>,
key: Option<String>,
}
impl SerializeMap for MultipartMapSerializer { impl SerializeMap for MultipartMapSerializer {
type Ok = <MultipartSerializer as SerializeStruct>::Ok; type Ok = Form;
type Error = Error; type Error = Error;
fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), Self::Error> fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), Self::Error>
where where
T: Serialize, 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(()) Ok(())
} }
@ -322,110 +266,90 @@ impl SerializeMap for MultipartMapSerializer {
where where
T: Serialize, 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 {})?; let part = value.serialize(PartSerializer {})?;
self.parts.push((key, part));
self.files.extend(file);
take_mut::take(&mut self.form, |f| f.part(key, part));
Ok(()) Ok(())
} }
fn end(self) -> Result<Self::Ok, Self::Error> { fn end(self) -> Result<Self::Ok, Self::Error> {
let form = self Ok(self.form)
.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))
}
} }
} }
struct PartSerializer {}
impl Serializer for PartSerializer { impl Serializer for PartSerializer {
type Ok = (Part, Vec<(String, InputFile)>); type Ok = Part;
type Error = Error; type Error = Error;
type SerializeSeq = InnerPartSerializer;
type SerializeStruct = JsonPartSerializer;
type SerializeSeq = JsonPartSerializer;
// Unimplemented
type SerializeTuple = Impossible<Self::Ok, Self::Error>; type SerializeTuple = Impossible<Self::Ok, Self::Error>;
type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>; type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>; type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
type SerializeMap = Impossible<Self::Ok, Self::Error>; type SerializeMap = Impossible<Self::Ok, Self::Error>;
type SerializeStruct = PartSerializerStruct; type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
type SerializeStructVariant = PartFromFile;
fn serialize_bool(self, v: bool) -> Result<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> { fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
unimplemented!() Ok(Part::text(v.to_string()))
} }
fn serialize_i16(self, _: i16) -> Result<Self::Ok, Self::Error> { fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
unimplemented!() Ok(Part::text(v.to_string()))
} }
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> { 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> { 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> { fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
unimplemented!() Ok(Part::text(v.to_string()))
} }
fn serialize_u16(self, _: u16) -> Result<Self::Ok, Self::Error> { fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
unimplemented!() Ok(Part::text(v.to_string()))
} }
fn serialize_u32(self, _: u32) -> Result<Self::Ok, Self::Error> { fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
unimplemented!() Ok(Part::text(v.to_string()))
} }
fn serialize_u64(self, _: u64) -> Result<Self::Ok, Self::Error> { fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
unimplemented!() Ok(Part::text(v.to_string()))
} }
fn serialize_f32(self, _: f32) -> Result<Self::Ok, Self::Error> { fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
unimplemented!() Ok(Part::text(v.to_string()))
} }
fn serialize_f64(self, _: f64) -> Result<Self::Ok, Self::Error> { fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
unimplemented!() Ok(Part::text(v.to_string()))
} }
fn serialize_char(self, _: char) -> Result<Self::Ok, Self::Error> { fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
unimplemented!() Ok(Part::text(v.to_string()))
} }
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> { 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> { fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
unimplemented!() Ok(Part::bytes(v.to_owned()))
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
unimplemented!()
} }
fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error> fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
@ -435,21 +359,48 @@ impl Serializer for PartSerializer {
value.serialize(self) 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( fn serialize_unit_variant(
self, self,
_: &'static str, _: &'static str,
_: u32, _: u32,
variant_name: &'static str, variant_name: &'static str,
) -> Result<Self::Ok, Self::Error> { ) -> 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>( fn serialize_newtype_struct<T: ?Sized>(
@ -465,37 +416,15 @@ impl Serializer for PartSerializer {
fn serialize_newtype_variant<T: ?Sized>( fn serialize_newtype_variant<T: ?Sized>(
self, self,
name: &'static str, _name: &'static str,
variant_index: u32, _variant_index: u32,
variant: &'static str, _variant: &'static str,
value: &T, _value: &T,
) -> Result<Self::Ok, Self::Error> ) -> Result<Self::Ok, Self::Error>
where where
T: Serialize, T: Serialize,
{ {
let file: InputFile = InputFileUnserializer::NotMem.serialize_newtype_variant( unimplemented!()
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![],
})
} }
fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple, Self::Error> { fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple, Self::Error> {
@ -524,44 +453,19 @@ impl Serializer for PartSerializer {
unimplemented!() 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( fn serialize_struct_variant(
self, self,
name: &'static str, _name: &'static str,
variant_index: u32, _variant_index: u32,
variant: &'static str, _variant: &'static str,
len: usize, _len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> { ) -> Result<Self::SerializeStructVariant, Self::Error> {
Ok(PartFromFile { unimplemented!()
inner: InputFileUnserializer::memory().serialize_struct_variant(
name,
variant_index,
variant,
len,
)?,
})
} }
} }
struct PartFromFile { impl SerializeStruct for JsonPartSerializer {
inner: InputFileUnserializer, type Ok = Part;
}
impl SerializeStructVariant for PartFromFile {
type Ok = (Part, Vec<(String, InputFile)>);
type Error = Error; type Error = Error;
fn serialize_field<T: ?Sized>( fn serialize_field<T: ?Sized>(
@ -572,158 +476,71 @@ impl SerializeStructVariant for PartFromFile {
where where
T: Serialize, T: Serialize,
{ {
self.inner use std::fmt::Write;
.serialize_field(key, value) use PartSerializerStructState::*;
.map_err(Error::InputFileUnserializer)
}
fn end(self) -> Result<Self::Ok, Self::Error> { let value = serde_json::to_string(value)?;
let file = self.inner.end()?; match self.state {
Empty => {
self.state = Rest;
// TODO: to method write!(&mut self.buf, "{{\"{}\":{}", key, 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![])), Rest => write!(&mut self.buf, ",\"{}\":{}", key, value)?,
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);
} }
Ok(()) Ok(())
} }
fn end(mut self) -> Result<Self::Ok, Self::Error> { fn end(mut self) -> Result<Self::Ok, Self::Error> {
let state = match self.1 { use PartSerializerStructState::*;
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)?;
let json = self.0.into_inner(); match self.state {
Ok((Part::bytes(json), self.2)) Empty => Ok(Part::text("{{}}")),
Rest => {
self.buf += "}";
Ok(Part::text(self.buf))
}
}
} }
} }
fn get_state( impl SerializeSeq for JsonPartSerializer {
compound: serde_json::ser::Compound<Vec<u8>, serde_json::ser::CompactFormatter>, type Ok = Part;
) -> serde_json::ser::State {
// Compound may have more variants under some serde_json features type Error = Error;
#[allow(unreachable_patterns)]
match compound { fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
serde_json::ser::Compound::Map { ser: _, state } => state, where
_ => unreachable!(), 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 bytes::{Bytes, BytesMut};
use serde::{Deserialize, Serialize}; 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. /// This object represents the contents of a file to be uploaded.
/// ///
/// [The official docs](https://core.telegram.org/bots/api#inputfile). /// [The official docs](https://core.telegram.org/bots/api#inputfile).
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Debug, Clone)]
#[non_exhaustive] pub struct InputFile {
pub enum InputFile { id: OnceCell<Arc<str>>,
file_name: Option<Cow<'static, str>>,
inner: InnerFile,
}
#[derive(Clone)]
enum InnerFile {
File(PathBuf), File(PathBuf),
Memory { Bytes(bytes::Bytes),
file_name: String, Url(url::Url),
data: Cow<'static, [u8]>,
},
Url(Url),
FileId(String), FileId(String),
} }
use InnerFile::*;
impl InputFile { impl InputFile {
pub fn file<P>(path: P) -> Self /// Creates an `InputFile` from an url.
where ///
P: Into<PathBuf>, /// Notes:
{ /// - When sending by URL the target file must have the correct MIME type
Self::File(path.into()) /// (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 /// Creates an `InputFile` from a file id.
where ///
S: Into<String>, /// File id can be obtained from
D: Into<Cow<'static, [u8]>>, ///
{ /// Notes:
Self::Memory { /// - It is not possible to change the file type when resending by file id.
file_name: file_name.into(), /// I.e. a video can't be sent as a photo, a photo can't be sent as a
data: data.into(), /// 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 { /// Returns id of this file.
Self::Url(url) ///
/// 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 /// Returns `true` if this file needs an attachment i.e. it's not a file_id
where /// or url that can be serialized without any additional multipart parts.
T: Into<String>, pub(crate) fn needs_attach(&self) -> bool {
{ !matches!(self.inner, Url(_) | FileId(_))
Self::FileId(file_id.into())
} }
pub fn as_file(&self) -> Option<&PathBuf> { /// Takes this file out.
match self { ///
Self::File(path) => Some(path), /// **Note**: this replaces `self` with a dummy value, this function should
_ => None, /// 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> { /// Takes the file name or tries to guess it based on file name in the path
match self { /// if `File.0`. Returns an empty string if couldn't guess.
Self::Url(url) => Some(url), fn take_or_guess_filename(&mut self) -> Cow<'static, str> {
_ => None, 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 { match self {
Self::FileId(id) => Some(id), File(path) => f.debug_struct("File").field("path", path).finish(),
_ => None, 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> { impl Serialize for InputFile {
fn from(file: InputFile) -> Self { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
match file { where
InputFile::File(path) => Some(path), S: serde::Serializer,
_ => None, {
} self.attach_or_value().serialize(serializer)
} }
} }
// internal api // internal api
use reqwest::multipart::Part;
impl InputFile { impl InputFile {
pub(crate) async fn into_part(self) -> std::io::Result<Part> { pub(crate) fn into_part(mut self) -> Option<impl Future<Output = Part>> {
use bytes::{Bytes, BytesMut}; let filename = self.take_or_guess_filename();
use reqwest::Body;
use tokio_util::codec::{Decoder, FramedRead};
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 { File(path_to_file) => {
type Item = Bytes; let fut = async {
type Error = std::io::Error; 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> { Body::wrap_stream(file)
if src.is_empty() { }
return Ok(None); Err(err) => {
} // explicit type needed for `Bytes: From<?T>` in `wrap_stream`
Ok(Some(src.split().freeze())) 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 { /// An internal trait that is used in expansion of `impl_payload!` used to work
Self::File(path_to_file) => { /// with input-file-like things (`InputFile` itself, `Option<InputFile>`,
let file_name = path_to_file /// `InputSticker`)
.file_name() pub(crate) trait InputFileLike {
.unwrap() fn copy_into(&self, into: &mut dyn FnMut(InputFile));
.to_string_lossy()
.into_owned();
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)) impl InputFileLike for InputFile {
} fn copy_into(&self, into: &mut dyn FnMut(InputFile)) {
Self::Memory { file_name, data } => Ok(Part::bytes(data).file_name(file_name)), into(self.clone())
Self::Url(s) => Ok(Part::text(String::from(s))), }
Self::FileId(s) => Ok(Part::text(s)),
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 std::iter;
use serde::{Deserialize, Serialize}; use serde::Serialize;
use crate::types::{InputFile, MessageEntity, ParseMode}; use crate::types::{InputFile, MessageEntity, ParseMode};
@ -533,7 +533,7 @@ mod tests {
#[test] #[test]
fn photo_serialize() { 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 { let photo = InputMedia::Photo(InputMediaPhoto {
media: InputFile::file_id("123456"), media: InputFile::file_id("123456"),
caption: None, caption: None,
@ -547,7 +547,7 @@ mod tests {
#[test] #[test]
fn video_serialize() { 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 { let video = InputMedia::Video(InputMediaVideo {
media: InputFile::file_id("123456"), media: InputFile::file_id("123456"),
thumb: None, thumb: None,
@ -566,7 +566,7 @@ mod tests {
#[test] #[test]
fn animation_serialize() { 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 { let video = InputMedia::Animation(InputMediaAnimation {
media: InputFile::file_id("123456"), media: InputFile::file_id("123456"),
thumb: None, thumb: None,
@ -584,7 +584,7 @@ mod tests {
#[test] #[test]
fn audio_serialize() { 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 { let video = InputMedia::Audio(InputMediaAudio {
media: InputFile::file_id("123456"), media: InputFile::file_id("123456"),
thumb: None, thumb: None,
@ -602,7 +602,7 @@ mod tests {
#[test] #[test]
fn document_serialize() { 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 { let video = InputMedia::Document(InputMediaDocument {
media: InputFile::file_id("123456"), media: InputFile::file_id("123456"),
thumb: None, thumb: None,