mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-10 20:12:25 +01:00
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:
parent
06f123710e
commit
b303958afd
10 changed files with 1245 additions and 21 deletions
|
@ -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"
|
||||
|
|
|
@ -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
38
src/local_macros.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
28
src/serde_multipart/mod.rs
Normal file
28
src/serde_multipart/mod.rs
Normal 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?) }
|
||||
}
|
678
src/serde_multipart/serializers.rs
Normal file
678
src/serde_multipart/serializers.rs
Normal 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!(),
|
||||
}
|
||||
}
|
83
src/serde_multipart/unserializers.rs
Normal file
83
src/serde_multipart/unserializers.rs
Normal 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));
|
||||
}
|
145
src/serde_multipart/unserializers/bytes.rs
Normal file
145
src/serde_multipart/unserializers/bytes.rs
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
173
src/serde_multipart/unserializers/input_file.rs
Normal file
173
src/serde_multipart/unserializers/input_file.rs
Normal 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?"),
|
||||
}
|
||||
}
|
||||
}
|
61
src/serde_multipart/unserializers/string.rs
Normal file
61
src/serde_multipart/unserializers/string.rs
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue