implement serde multipart serializer

Serde multipart serializer can be used to serialize requests
into multipart/form-data without need for copy-paste and boilerplate
This commit is contained in:
Waffle 2020-08-16 00:07:19 +03:00
parent 06f123710e
commit b303958afd
10 changed files with 1245 additions and 21 deletions

View file

@ -17,7 +17,7 @@ authors = [
[dependencies]
futures = "0.3.5"
tokio = { version = "0.2.21", features = ["fs", "stream"] }
tokio = { version = "0.2.21", features = ["fs", "stream", "rt-threaded", "macros"] }
tokio-util = "0.3.1"
bytes = "0.5.5"
async-trait = "0.1.36"
@ -26,6 +26,7 @@ reqwest = { version = "0.10.6", features = ["json", "stream"] }
serde = { version = "1.0.114", features = ["derive"] }
serde_json = "1.0.55"
serde_with_macros = "1.1.0"
uuid = { version = "0.8.1", features = ["v4"] } # for attaching input files
derive_more = "0.99.9"
mime = "0.3.16"

View file

@ -11,6 +11,9 @@
#![forbid(unsafe_code)]
//#![deny(missing_docs)]
#[macro_use]
mod local_macros; // internal helper macros
pub use self::{
bot::{Bot, BotBuilder},
errors::{ApiErrorKind, DownloadError, KnownApiErrorKind, RequestError},
@ -25,6 +28,7 @@ mod errors;
// implementation details
mod net;
mod serde_multipart;
/// Constructs a client from the `TELOXIDE_PROXY` environmental variable.
///

38
src/local_macros.rs Normal file
View file

@ -0,0 +1,38 @@
#[macro_use]
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,
})
}
)+
};
}

View file

@ -0,0 +1,28 @@
//! Module for serializing into `multipart/form-data`
//! ([`reqwest::multipart::Form`])
//!
//! [`reqwest::multipart::Form`]: reqwest::multipart::Form
//!
//! ## How it works
//!
//! You better not know...
mod serializers;
mod unserializers;
use std::future::Future;
use reqwest::multipart::Form;
use serde::Serialize;
use serializers::MultipartTopLvlSerializer;
pub(crate) use serializers::Error;
/// Serializes given value into [`Form`]
///
/// [`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?) }
}

View file

@ -0,0 +1,678 @@
use crate::{
serde_multipart::unserializers::{InputFileUnserializer, StringUnserializer},
types::InputFile,
};
use futures::{
future::{ready, BoxFuture},
stream::FuturesUnordered,
FutureExt, StreamExt, TryStreamExt,
};
use reqwest::multipart::{Form, Part};
use serde::{
ser,
ser::{Impossible, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant},
Serialize, Serializer,
};
use std::{fmt, fmt::Display, io};
#[derive(Debug, derive_more::From)]
pub(crate) enum Error {
Custom(String),
TopLevelNotStruct,
InputFileUnserializerError(crate::serde_multipart::unserializers::UnserializerError),
Io(std::io::Error),
Json(serde_json::Error),
}
impl ser::Error for Error {
fn custom<T>(msg: T) -> Self
where
T: Display,
{
Self::Custom(msg.to_string())
}
}
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::InputFileUnserializerError(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
}
}
pub(crate) struct MultipartTopLvlSerializer {}
impl Serializer for MultipartTopLvlSerializer {
type Ok = <MultipartSerializer as SerializeStruct>::Ok;
type Error = Error;
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_bool(self, _: bool) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_i8(self, _: i8) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_i16(self, _: i16) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_i32(self, _: i32) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_i64(self, _: i64) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_u8(self, _: u8) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_u16(self, _: u16) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_u32(self, _: u32) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_u64(self, _: u64) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_f32(self, _: f32) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_f64(self, _: f64) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_char(self, _: char) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_str(self, _: &str) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_bytes(self, _: &[u8]) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_some<T: ?Sized>(self, _: &T) -> Result<Self::Ok, Self::Error>
where
T: Serialize,
{
Err(Error::TopLevelNotStruct)
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_unit_struct(self, _: &'static str) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_unit_variant(
self,
_: &'static str,
_: u32,
_: &'static str,
) -> Result<Self::Ok, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_newtype_struct<T: ?Sized>(
self,
_: &'static str,
_: &T,
) -> Result<Self::Ok, Self::Error>
where
T: Serialize,
{
Err(Error::TopLevelNotStruct)
}
fn serialize_newtype_variant<T: ?Sized>(
self,
_: &'static str,
_: u32,
_: &'static str,
_: &T,
) -> Result<Self::Ok, Self::Error>
where
T: Serialize,
{
Err(Error::TopLevelNotStruct)
}
fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_tuple_struct(
self,
_: &'static str,
_: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
Err(Error::TopLevelNotStruct)
}
fn serialize_tuple_variant(
self,
_: &'static str,
_: u32,
_: &'static str,
_: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
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,
_: u32,
_: &'static str,
_: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
Err(Error::TopLevelNotStruct)
}
}
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 Error = Error;
fn serialize_field<T: ?Sized>(
&mut self,
key: &'static str,
value: &T,
) -> Result<(), Self::Error>
where
T: Serialize,
{
let (part, file) = value.serialize(PartSerializer {})?;
self.parts.push((key, part));
self.files.extend(file);
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))
}
}
}
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 Error = Error;
fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
self.key = Some(key.serialize(StringUnserializer)?);
Ok(())
}
fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
let key = self.key.take().unwrap();
let (part, file) = value.serialize(PartSerializer {})?;
self.parts.push((key, part));
self.files.extend(file);
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))
}
}
}
struct PartSerializer {}
impl Serializer for PartSerializer {
type Ok = (Part, Vec<(String, InputFile)>);
type Error = Error;
type SerializeSeq = InnerPartSerializer;
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;
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
Ok((Part::text(v.to_string()), Vec::new()))
}
fn serialize_i8(self, _: i8) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_i16(self, _: i16) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
Ok((Part::text(v.to_string()), Vec::new()))
}
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
Ok((Part::text(v.to_string()), Vec::new()))
}
fn serialize_u8(self, _: u8) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_u16(self, _: u16) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_u32(self, _: u32) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_u64(self, _: u64) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_f32(self, _: f32) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_f64(self, _: f64) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_char(self, _: char) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
Ok((Part::text(v.to_owned()), Vec::new()))
}
fn serialize_bytes(self, _: &[u8]) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: Serialize,
{
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()))
}
fn serialize_newtype_struct<T: ?Sized>(
self,
_: &'static str,
_: &T,
) -> Result<Self::Ok, Self::Error>
where
T: Serialize,
{
unimplemented!()
}
fn serialize_newtype_variant<T: ?Sized>(
self,
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) | InputFile::Url(s) => Ok((Part::text(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> {
unimplemented!()
}
fn serialize_tuple_struct(
self,
_: &'static str,
_: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
unimplemented!()
}
fn serialize_tuple_variant(
self,
_: &'static str,
_: u32,
_: &'static str,
_: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
unimplemented!()
}
fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
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,
) -> Result<Self::SerializeStructVariant, Self::Error> {
Ok(PartFromFile {
inner: InputFileUnserializer::memory().serialize_struct_variant(
name,
variant_index,
variant,
len,
)?,
})
}
}
struct PartFromFile {
inner: InputFileUnserializer,
}
impl SerializeStructVariant for PartFromFile {
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,
{
self.inner.serialize_field(key, value).map_err(Error::InputFileUnserializerError)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
let file = self.inner.end()?;
// 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)]))
}
InputFile::FileId(s) | InputFile::Url(s) => Ok((Part::text(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)?;
let file: InputFile = serde_json::from_value(value["media"].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) | InputFile::Url(s) => {
value["media"] = serde_json::Value::String(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) | InputFile::Url(s) => {
SerializeStruct::serialize_field(&mut ser, key, &s)?;
self.1 = get_state(ser)
}
}
} else {
SerializeStruct::serialize_field(&mut ser, key, value)?;
self.1 = get_state(ser);
}
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)?;
let json = self.0.into_inner();
Ok((Part::bytes(json), self.2))
}
}
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!(),
}
}

View file

@ -0,0 +1,83 @@
mod bytes;
mod input_file;
mod string;
pub(crate) use input_file::InputFileUnserializer;
pub(crate) use string::StringUnserializer;
use std::fmt::{self, Display};
use serde::ser;
#[derive(Debug, PartialEq, Eq)]
pub enum UnserializerError {
Custom(String),
UnsupportedType { ty: &'static str, supported: &'static str },
UnexpectedField { name: &'static str, expected: &'static [&'static str] },
UnexpectedVariant { name: &'static str, expected: &'static [&'static str] },
WrongLen { len: usize, expected: usize },
}
impl ser::Error for UnserializerError {
fn custom<T>(msg: T) -> Self
where
T: Display,
{
Self::Custom(msg.to_string())
}
}
impl Display for UnserializerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnexpectedField { name, expected } => write!(
f,
"Unexpected field: `{}`, expected field(s): `{}`",
name,
expected.join(", ")
),
Self::Custom(s) => write!(f, "Custom serde error: {}", s),
Self::UnsupportedType { ty, supported } => {
write!(f, "Unsupported type: `{}`, supported type(s): `{}`", ty, supported)
}
Self::UnexpectedVariant { name, expected } => write!(
f,
"Unexpected variant: `{}`, expected variants(s): `{}`",
name,
expected.join(", ")
),
Self::WrongLen { len, expected } => {
write!(f, "Wrong len: `{}`, expected `{}`", len, expected)
}
}
}
}
impl std::error::Error for UnserializerError {}
#[test]
fn test() {
use crate::serde_multipart::unserializers::string::StringUnserializer;
use serde::Serialize;
use crate::{
serde_multipart::unserializers::input_file::InputFileUnserializer, types::InputFile,
};
use std::borrow::Cow;
let value = String::from("test");
assert_eq!(value.serialize(StringUnserializer), Ok(value));
let value = InputFile::Url(String::from("url"));
assert_eq!(value.serialize(InputFileUnserializer::NotMem), Ok(value));
let value = InputFile::FileId(String::from("file_id"));
assert_eq!(value.serialize(InputFileUnserializer::NotMem), Ok(value));
let value =
InputFile::Memory { file_name: String::from("name"), data: Cow::Owned(vec![1, 2, 3]) };
assert_eq!(value.serialize(InputFileUnserializer::memory()), Ok(value));
let value = InputFile::File("a/b/c".into());
assert_eq!(value.serialize(InputFileUnserializer::NotMem), Ok(value));
}

View file

@ -0,0 +1,145 @@
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

@ -0,0 +1,173 @@
use std::borrow::Cow;
use serde::{
ser::{Impossible, SerializeStructVariant},
Serialize, Serializer,
};
use crate::{
serde_multipart::unserializers::{
bytes::BytesUnserializer, string::StringUnserializer, UnserializerError,
},
types::InputFile,
};
pub(crate) enum InputFileUnserializer {
Memory { file_name: String, data: Cow<'static, [u8]> },
NotMem,
}
impl InputFileUnserializer {
pub(crate) fn memory() -> Self {
Self::Memory { file_name: String::new(), data: Cow::Borrowed(&[]) }
}
}
impl Serializer for InputFileUnserializer {
type Ok = InputFile;
type Error = UnserializerError;
type SerializeSeq = Impossible<InputFile, UnserializerError>;
type SerializeTuple = Impossible<InputFile, UnserializerError>;
type SerializeTupleStruct = Impossible<InputFile, UnserializerError>;
type SerializeTupleVariant = Impossible<InputFile, UnserializerError>;
type SerializeMap = Impossible<InputFile, UnserializerError>;
type SerializeStruct = Impossible<InputFile, UnserializerError>;
type SerializeStructVariant = Self;
fn serialize_newtype_variant<T: ?Sized>(
self,
name: &'static str,
_: u32,
variant: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: Serialize,
{
if name != "InputFile" {
return Err(UnserializerError::UnsupportedType {
ty: name,
supported: "InputFile", // TODO
});
}
// TODO
match variant {
"File" => Ok(InputFile::File(value.serialize(StringUnserializer)?.into())),
"Url" => Ok(InputFile::Url(value.serialize(StringUnserializer)?)),
"FileId" => Ok(InputFile::FileId(value.serialize(StringUnserializer)?)),
name => Err(UnserializerError::UnexpectedVariant {
name,
expected: &["File", "Url", "FileId"], // TODO
}),
}
}
fn serialize_struct_variant(
self,
name: &'static str,
_variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
if name != "InputFile" {
return Err(UnserializerError::UnsupportedType { ty: name, supported: "InputFile" });
}
if variant != "Memory" {
return Err(UnserializerError::UnexpectedVariant {
name: variant,
expected: &["Memory"],
});
}
if len != 2 {
return Err(UnserializerError::WrongLen { len, expected: 2 });
}
Ok(self)
}
forward_to_unsuported_ty! {
supported: "Newtype variant, struct variant";
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]
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_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"
}
}
}
impl SerializeStructVariant for InputFileUnserializer {
type Ok = InputFile;
type Error = UnserializerError;
fn serialize_field<T: ?Sized>(
&mut self,
key: &'static str,
value: &T,
) -> Result<(), Self::Error>
where
T: Serialize,
{
let (file_name, data) = match self {
Self::Memory { file_name, data } => (file_name, data),
Self::NotMem => {
*self = Self::memory();
match self {
Self::Memory { file_name, data } => (file_name, data),
Self::NotMem => unreachable!(),
}
}
};
match key {
"file_name" => *file_name = value.serialize(StringUnserializer)?,
"data" => *data = Cow::Owned(value.serialize(BytesUnserializer::default())?),
name => {
return Err(UnserializerError::UnexpectedField {
name,
expected: &["file_name", "data"], // TODO
});
}
}
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
match self {
Self::Memory { file_name, data } => Ok(InputFile::Memory { file_name, data }),
Self::NotMem => unreachable!("struct without fields?"),
}
}
}

View file

@ -0,0 +1,61 @@
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

@ -5,7 +5,7 @@ use std::{borrow::Cow, path::PathBuf};
/// This object represents the contents of a file to be uploaded.
///
/// [The official docs](https://core.telegram.org/bots/api#inputfile).
#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize)]
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum InputFile {
File(PathBuf),
@ -75,27 +75,40 @@ impl From<InputFile> for Option<PathBuf> {
}
}
impl Serialize for InputFile {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::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};
struct FileDecoder;
impl Decoder for FileDecoder {
type Item = Bytes;
type Error = std::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 {
InputFile::File(path) => {
// NOTE: file should be actually attached with
// multipart/form-data
serializer.serialize_str(
// TODO: remove unwrap (?)
&format!("attach://{}", path.file_name().unwrap().to_string_lossy()),
)
Self::File(path_to_file) => {
let file_name = path_to_file.file_name().unwrap().to_string_lossy().into_owned();
let file = FramedRead::new(tokio::fs::File::open(path_to_file).await?, FileDecoder);
Ok(Part::stream(Body::wrap_stream(file)).file_name(file_name))
}
InputFile::Memory { data, .. } => {
// NOTE: file should be actually attached with
// multipart/form-data
serializer.serialize_str(&format!("attach://{}", String::from_utf8_lossy(data)))
}
InputFile::Url(url) => serializer.serialize_str(url),
InputFile::FileId(id) => serializer.serialize_str(id),
Self::Memory { file_name, data } => Ok(Part::bytes(data).file_name(file_name)),
Self::Url(s) | Self::FileId(s) => Ok(Part::text(s)),
}
}
}