Merge pull request #35 from teloxide/custom_url

Add `Bot::{set_,}api_url` methods
This commit is contained in:
Waffle Lapkin 2020-12-30 17:42:43 +03:00 committed by GitHub
commit 8f3bb9d96b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 782 additions and 242 deletions

View file

@ -2,5 +2,4 @@ format_code_in_doc_comments = true
wrap_comments = true
format_strings = true
merge_imports = true
use_small_heuristics = "Max"
use_field_init_shorthand = true

View file

@ -31,5 +31,3 @@ pub use cache_me::CacheMe;
pub use throttle::Throttle;
pub use parse_mode::DefaultParseMode;
// FIXME: move default `parse_mode` to adaptor

View file

@ -30,7 +30,10 @@ impl<B> CacheMe<B> {
///
/// [`RequesterExt::cache_me`]: crate::requests::RequesterExt::cache_me
pub fn new(bot: B) -> CacheMe<B> {
Self { bot, me: Arc::new(OnceCell::new()) }
Self {
bot,
me: Arc::new(OnceCell::new()),
}
}
/// Allows to access inner bot

View file

@ -18,7 +18,10 @@ impl<B> DefaultParseMode<B> {
///
/// [`RequesterExt::parse_mode`]: crate::requests::RequesterExt::parse_mode
pub fn new(bot: B, parse_mode: ParseMode) -> Self {
Self { bot, mode: parse_mode }
Self {
bot,
mode: parse_mode,
}
}
/// Allows to access the inner bot.

View file

@ -99,7 +99,11 @@ pub struct Limits {
/// [tgdoc]: https://core.telegram.org/bots/faq#my-bot-is-hitting-limits-how-do-i-avoid-this
impl Default for Limits {
fn default() -> Self {
Self { chat_s: 1, overall_s: 30, chat_m: 20 }
Self {
chat_s: 1,
overall_s: 30,
chat_m: 20,
}
}
}
@ -244,7 +248,10 @@ async fn worker(limits: Limits, mut queue_rx: mpsc::Receiver<(Id, Sender<Never>)
// as truncates which is ok since in case of truncation it would always be >=
// limits.overall_s
let used = history.iter().take_while(|(_, time)| time > &sec_back).count() as u32;
let used = history
.iter()
.take_while(|(_, time)| time > &sec_back)
.count() as u32;
let mut allowed = limits.overall_s.saturating_sub(used);
if allowed == 0 {
@ -307,7 +314,10 @@ impl<B> Throttle<B> {
let (queue_tx, queue_rx) = mpsc::channel(buffer as usize);
let worker = worker(limits, queue_rx);
let this = Self { bot, queue: queue_tx };
let this = Self {
bot,
queue: queue_tx,
};
(this, worker)
}
@ -526,7 +536,11 @@ where
let (tx, rx) = channel();
let id = self.2(self.payload_ref());
let send = self.1.send_t((id, tx));
ThrottlingSend(ThrottlingSendInner::Registering { request: self.0, send, wait: rx })
ThrottlingSend(ThrottlingSendInner::Registering {
request: self.0,
send,
wait: rx,
})
}
fn send_ref(&self) -> Self::SendRef {
@ -541,7 +555,11 @@ where
// should **not** do any kind of work, so it's ok.
let request = self.0.send_ref();
ThrottlingSendRef(ThrottlingSendRefInner::Registering { request, send, wait: rx })
ThrottlingSendRef(ThrottlingSendRefInner::Registering {
request,
send,
wait: rx,
})
}
}
@ -575,11 +593,18 @@ impl<R: Request> Future for ThrottlingSend<R> {
let mut this = self.as_mut().project().0;
match this.as_mut().project() {
SendProj::Registering { request: _, send, wait: _ } => match send.poll(cx) {
SendProj::Registering {
request: _,
send,
wait: _,
} => match send.poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(res) => {
if let SendRepl::Registering { request, send: _, wait } =
this.as_mut().project_replace(ThrottlingSendInner::Done)
if let SendRepl::Registering {
request,
send: _,
wait,
} = this.as_mut().project_replace(ThrottlingSendInner::Done)
{
match res {
Ok(()) => this
@ -588,9 +613,9 @@ impl<R: Request> Future for ThrottlingSend<R> {
// The worker is unlikely to drop queue before sending all requests,
// but just in case it has dropped the queue, we want to just send the
// request.
Err(_) => this
.as_mut()
.project_replace(ThrottlingSendInner::Sent { fut: request.send() }),
Err(_) => this.as_mut().project_replace(ThrottlingSendInner::Sent {
fut: request.send(),
}),
};
}
@ -607,8 +632,9 @@ impl<R: Request> Future for ThrottlingSend<R> {
if let SendRepl::Pending { request, wait: _ } =
this.as_mut().project_replace(ThrottlingSendInner::Done)
{
this.as_mut()
.project_replace(ThrottlingSendInner::Sent { fut: request.send() });
this.as_mut().project_replace(ThrottlingSendInner::Sent {
fut: request.send(),
});
}
self.poll(cx)
@ -654,11 +680,18 @@ impl<R: Request> Future for ThrottlingSendRef<R> {
let mut this = self.as_mut().project().0;
match this.as_mut().project() {
SendRefProj::Registering { request: _, send, wait: _ } => match send.poll(cx) {
SendRefProj::Registering {
request: _,
send,
wait: _,
} => match send.poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(res) => {
if let SendRefRepl::Registering { request, send: _, wait } =
this.as_mut().project_replace(ThrottlingSendRefInner::Done)
if let SendRefRepl::Registering {
request,
send: _,
wait,
} = this.as_mut().project_replace(ThrottlingSendRefInner::Done)
{
match res {
Ok(()) => this

View file

@ -25,7 +25,10 @@ impl Requester for Bot {
U: Into<String>,
A: IntoIterator<Item = crate::types::AllowedUpdate>,
{
Self::SetWebhook::new(self.clone(), payloads::SetWebhook::new(url, allowed_updates))
Self::SetWebhook::new(
self.clone(),
payloads::SetWebhook::new(url, allowed_updates),
)
}
type DeleteWebhook = JsonRequest<payloads::DeleteWebhook>;
@ -116,7 +119,10 @@ impl Requester for Bot {
where
C: Into<ChatId>,
{
Self::SendAnimation::new(self.clone(), payloads::SendAnimation::new(chat_id, animation))
Self::SendAnimation::new(
self.clone(),
payloads::SendAnimation::new(chat_id, animation),
)
}
type SendVoice = MultipartRequest<payloads::SendVoice>;
@ -134,7 +140,10 @@ impl Requester for Bot {
where
C: Into<ChatId>,
{
Self::SendVideoNote::new(self.clone(), payloads::SendVideoNote::new(chat_id, video_note))
Self::SendVideoNote::new(
self.clone(),
payloads::SendVideoNote::new(chat_id, video_note),
)
}
type SendMediaGroup = MultipartRequest<payloads::SendMediaGroup>;
@ -331,7 +340,10 @@ impl Requester for Bot {
where
C: Into<ChatId>,
{
Self::KickChatMember::new(self.clone(), payloads::KickChatMember::new(chat_id, user_id))
Self::KickChatMember::new(
self.clone(),
payloads::KickChatMember::new(chat_id, user_id),
)
}
type UnbanChatMember = JsonRequest<payloads::UnbanChatMember>;
@ -340,7 +352,10 @@ impl Requester for Bot {
where
C: Into<ChatId>,
{
Self::UnbanChatMember::new(self.clone(), payloads::UnbanChatMember::new(chat_id, user_id))
Self::UnbanChatMember::new(
self.clone(),
payloads::UnbanChatMember::new(chat_id, user_id),
)
}
type RestrictChatMember = JsonRequest<payloads::RestrictChatMember>;
@ -458,7 +473,10 @@ impl Requester for Bot {
where
C: Into<ChatId>,
{
Self::PinChatMessage::new(self.clone(), payloads::PinChatMessage::new(chat_id, message_id))
Self::PinChatMessage::new(
self.clone(),
payloads::PinChatMessage::new(chat_id, message_id),
)
}
type UnpinChatMessage = JsonRequest<payloads::UnpinChatMessage>;
@ -713,7 +731,10 @@ impl Requester for Bot {
where
C: Into<ChatId>,
{
Self::DeleteMessage::new(self.clone(), payloads::DeleteMessage::new(chat_id, message_id))
Self::DeleteMessage::new(
self.clone(),
payloads::DeleteMessage::new(chat_id, message_id),
)
}
type SendSticker = MultipartRequest<payloads::SendSticker>;
@ -903,7 +924,10 @@ impl Requester for Bot {
where
G: Into<String>,
{
Self::SendGame::new(self.clone(), payloads::SendGame::new(chat_id, game_short_name))
Self::SendGame::new(
self.clone(),
payloads::SendGame::new(chat_id, game_short_name),
)
}
type SetGameScore = JsonRequest<payloads::SetGameScore>;

View file

@ -1,9 +1,9 @@
use std::sync::Arc;
#[derive(Debug, Clone)]
pub(crate) enum ApiUrl {
Default,
// FIXME: remove #[allow] when we use this variant
#[allow(dead_code)]
Custom(reqwest::Url),
Custom(Arc<reqwest::Url>),
}
impl ApiUrl {
@ -12,7 +12,7 @@ impl ApiUrl {
// FIXME(waffle): parse once
ApiUrl::Default => reqwest::Url::parse(crate::net::TELEGRAM_API_URL)
.expect("failed to parse default url"),
ApiUrl::Custom(url) => url.clone(),
ApiUrl::Custom(url) => (&**url).clone(),
}
}
}

View file

@ -20,7 +20,14 @@ impl<'w> Download<'w> for Bot {
path: &str,
destination: &'w mut (dyn AsyncWrite + Unpin + Send),
) -> Self::Fut {
net::download_file(&self.client, self.api_url.get(), &self.token, path, destination).boxed()
net::download_file(
&self.client,
self.api_url.get(),
&self.token,
path,
destination,
)
.boxed()
}
type StreamErr = reqwest::Error;

View file

@ -100,15 +100,47 @@ impl Bot {
Self::with_client(&get_env(TELOXIDE_TOKEN), client)
}
/// Returns currently used token
/// Sets a custom API URL.
///
/// For example, you can run your own [Telegram bot API server][tbas] and
/// set its URL using this method.
///
/// [tbas]: https://github.com/tdlib/telegram-bot-api
///
/// ## Examples
///
/// ```
/// use teloxide_core::{
/// requests::{Request, Requester},
/// Bot,
/// };
///
/// # async {
/// let url = reqwest::Url::parse("https://localhost/tbas").unwrap();
/// let bot = Bot::new("TOKEN").set_api_url(url);
/// // From now all methods will use "https://localhost/tbas" as an API URL.
/// bot.get_me().send().await
/// # };
/// ```
pub fn set_api_url(mut self, url: reqwest::Url) -> Self {
self.api_url = ApiUrl::Custom(Arc::new(url));
self
}
/// Returns currently used token.
pub fn token(&self) -> &str {
&self.token
}
/// Returns currently used http-client
/// Returns currently used http-client.
pub fn client(&self) -> &Client {
&self.client
}
/// Returns currently used token API url.
pub fn api_url(&self) -> reqwest::Url {
self.api_url.get()
}
}
impl Bot {

View file

@ -72,9 +72,10 @@ pub enum ApiError {
/// 1. [`EditMessageText`]
///
/// [`EditMessageText`]: crate::payloads::EditMessageText
#[serde(rename = "Bad Request: message is not modified: specified new message content and \
reply markup are exactly the same as a current content and reply markup \
of the message")]
#[serde(
rename = "Bad Request: message is not modified: specified new message content and reply \
markup are exactly the same as a current content and reply markup of the message"
)]
MessageNotModified,
/// Occurs when bot tries to forward or delete a message which was deleted.
@ -294,8 +295,10 @@ pub enum ApiError {
/// 1. [`AnswerCallbackQuery`]
///
/// [`AnswerCallbackQuery`]: crate::payloads::AnswerCallbackQuery
#[serde(rename = "Bad Request: query is too old and response timeout expired or query id is \
invalid")]
#[serde(
rename = "Bad Request: query is too old and response timeout expired or query id is \
invalid"
)]
InvalidQueryID,
/// Occurs when bot tries to send InlineKeyboardMarkup with invalid button
@ -323,8 +326,10 @@ pub enum ApiError {
/// 1. [`SendMessage`]
///
/// [`SendMessage`]: crate::payloads::SendMessage
#[serde(rename = "Bad Request: can't parse inline keyboard button: Text buttons are \
unallowed in the inline keyboard")]
#[serde(
rename = "Bad Request: can't parse inline keyboard button: Text buttons are unallowed in \
the inline keyboard"
)]
TextButtonsAreUnallowed,
/// Occurs when bot tries to get file by wrong file id.
@ -417,8 +422,10 @@ pub enum ApiError {
/// 1. [`SetWebhook`]
///
/// [`SetWebhook`]: crate::payloads::SetWebhook
#[serde(rename = "Bad Request: bad webhook: Webhook can be set up only on ports 80, 88, 443 \
or 8443")]
#[serde(
rename = "Bad Request: bad webhook: Webhook can be set up only on ports 80, 88, 443 or \
8443"
)]
BadWebhookPort,
/// Occurs when bot tries to set webhook to unknown host.
@ -427,7 +434,9 @@ pub enum ApiError {
/// 1. [`SetWebhook`]
///
/// [`SetWebhook`]: crate::payloads::SetWebhook
#[serde(rename = "Bad Request: bad webhook: Failed to resolve host: Name or service not known")]
#[serde(
rename = "Bad Request: bad webhook: Failed to resolve host: Name or service not known"
)]
UnknownHost,
/// Occurs when bot tries to set webhook to invalid URL.
@ -509,8 +518,10 @@ pub enum ApiError {
/// 1. [`GetUpdates`]
///
/// [`GetUpdates`]: crate::payloads::GetUpdates
#[serde(rename = "Conflict: terminated by other getUpdates request; make sure that only one \
bot instance is running")]
#[serde(
rename = "Conflict: terminated by other getUpdates request; make sure that only one bot \
instance is running"
)]
TerminatedByOtherGetUpdates,
/// Occurs when bot tries to get file by invalid file id.

View file

@ -17,7 +17,6 @@
#[macro_use]
mod local_macros;
// FIXME(waffle): rethink modules, find a place for wrappers.
pub use self::{
bot::Bot,
errors::{ApiError, DownloadError, RequestError},

View file

@ -94,15 +94,18 @@ pub fn download_file<'o, D>(
where
D: ?Sized + AsyncWrite + Unpin,
{
client.get(file_url(api_url, token, path)).send().then(move |r| async move {
let mut res = r?.error_for_status()?;
client
.get(file_url(api_url, token, path))
.send()
.then(move |r| async move {
let mut res = r?.error_for_status()?;
while let Some(chunk) = res.chunk().await? {
dst.write_all(&chunk).await?;
}
while let Some(chunk) = res.chunk().await? {
dst.write_all(&chunk).await?;
}
Ok(())
})
Ok(())
})
}
/// Download a file from Telegram as [`Stream`].
@ -116,8 +119,11 @@ pub fn download_file_stream(
token: &str,
path: &str,
) -> impl Stream<Item = reqwest::Result<Bytes>> + 'static {
client.get(file_url(api_url, token, path)).send().into_stream().flat_map(|res| {
match res.and_then(Response::error_for_status) {
client
.get(file_url(api_url, token, path))
.send()
.into_stream()
.flat_map(|res| match res.and_then(Response::error_for_status) {
Ok(res) => Either::Left(unfold(res, |mut res| async {
match res.chunk().await {
Err(err) => Some((Err(err), res)),
@ -126,6 +132,5 @@ pub fn download_file_stream(
}
})),
Err(err) => Either::Right(once(ready(Err(err)))),
}
})
})
}

View file

@ -18,16 +18,24 @@ pub const TELEGRAM_API_URL: &str = "https://api.telegram.org";
///
/// [Telegram documentation]: https://core.telegram.org/bots/api#making-requests
fn method_url(base: reqwest::Url, token: &str, method_name: &str) -> reqwest::Url {
base.join(&format!("/bot{token}/{method}", token = token, method = method_name))
.expect("failed to format url")
base.join(&format!(
"/bot{token}/{method}",
token = token,
method = method_name
))
.expect("failed to format url")
}
/// Creates URL for downloading a file. See the [Telegram documentation].
///
/// [Telegram documentation]: https://core.telegram.org/bots/api#file
fn file_url(base: reqwest::Url, token: &str, file_path: &str) -> reqwest::Url {
base.join(&format!("file/bot{token}/{file}", token = token, file = file_path))
.expect("failed to format url")
base.join(&format!(
"file/bot{token}/{file}",
token = token,
file = file_path
))
.expect("failed to format url")
}
#[cfg(test)]

View file

@ -33,7 +33,12 @@ impl<R> Into<ResponseResult<R>> for TelegramResponse<R> {
fn into(self) -> Result<R, RequestError> {
match self {
TelegramResponse::Ok { response, .. } => Ok(response),
TelegramResponse::Err { error, error_code, response_parameters, .. } => {
TelegramResponse::Err {
error,
error_code,
response_parameters,
..
} => {
if let Some(params) = response_parameters {
match params {
ResponseParameters::RetryAfter(i) => Err(RequestError::RetryAfter(i)),
@ -64,7 +69,10 @@ mod tests {
assert!(matches!(
val,
TelegramResponse::Err { error: ApiError::TerminatedByOtherGetUpdates, .. }
TelegramResponse::Err {
error: ApiError::TerminatedByOtherGetUpdates,
..
}
));
}

View file

@ -39,16 +39,20 @@ pub trait Request: HasPayload {
/// Send this request.
///
/// ## Examples
// FIXME(waffle): ignored until full request redesign lands
/// ```ignore
/// ```
/// # async {
/// use teloxide_core::{methods::GetMe, requests::{Request, RequestJson}, types::User, bot::Bot};
/// use teloxide_core::{
/// payloads::GetMe,
/// requests::{JsonRequest, Request},
/// types::User,
/// Bot,
/// };
///
/// let bot = Bot::new("TOKEN");
/// let method = GetMe::new();
/// let request = JsonRequest::new(bot, method);
/// let _: User = request.send().await.unwrap();
/// # }
/// # };
/// ```
fn send(self) -> Self::Send;
@ -63,20 +67,19 @@ pub trait Request: HasPayload {
/// and then serializing it, this method should just serialize the data.)
///
/// ## Examples
// FIXME(waffle): ignored until full request redesign lands
/// ```ignore
/// ```
/// # async {
/// use teloxide_core::prelude::*;
/// use teloxide_core::{prelude::*, requests::Request, Bot};
///
/// let bot = Bot::new("TOKEN");
/// # let chat_ids = vec![1, 2, 3, 4].into_iter().map(Into::into);
/// # let chat_ids = vec![1i64, 2, 3, 4].into_iter().map(Into::into);
///
/// let mut req = bot.send_message(0, "Hi there!");
/// for chat_id in chat_ids {
/// req.chat_id = chat_id;
/// req.send_ref().await.unwrap();
/// }
/// # }
/// # };
/// ```
fn send_ref(&self) -> Self::SendRef;
}

View file

@ -216,7 +216,11 @@ impl Serializer for MultipartTopLvlSerializer {
}
fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Ok(MultipartMapSerializer { parts: vec![], files: vec![], key: None })
Ok(MultipartMapSerializer {
parts: vec![],
files: vec![],
key: None,
})
}
fn serialize_struct(
@ -245,7 +249,10 @@ pub(crate) struct MultipartSerializer {
impl MultipartSerializer {
fn new() -> Self {
Self { parts: Vec::new(), files: vec![] }
Self {
parts: Vec::new(),
files: vec![],
}
}
}
@ -269,8 +276,10 @@ impl SerializeStruct for MultipartSerializer {
}
fn end(self) -> Result<Self::Ok, Self::Error> {
let form =
self.parts.into_iter().fold(Form::new(), |acc, (key, value)| acc.part(key, value));
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))))
@ -322,8 +331,10 @@ impl SerializeMap for MultipartMapSerializer {
}
fn end(self) -> Result<Self::Ok, Self::Error> {
let form =
self.parts.into_iter().fold(Form::new(), |acc, (key, value)| acc.part(key, value));
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))))
@ -479,7 +490,10 @@ impl Serializer for PartSerializer {
}
fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Ok(Self::SerializeSeq { array_json_parts: vec![], files: vec![] })
Ok(Self::SerializeSeq {
array_json_parts: vec![],
files: vec![],
})
}
fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple, Self::Error> {
@ -556,7 +570,9 @@ impl SerializeStructVariant for PartFromFile {
where
T: Serialize,
{
self.inner.serialize_field(key, value).map_err(Error::InputFileUnserializer)
self.inner
.serialize_field(key, value)
.map_err(Error::InputFileUnserializer)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
@ -638,7 +654,10 @@ impl SerializeStruct for PartSerializerStruct {
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 };
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" {
@ -673,7 +692,10 @@ impl SerializeStruct for PartSerializerStruct {
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 };
let ser = serde_json::ser::Compound::Map {
ser: &mut self.0,
state,
};
SerializeStruct::end(ser)?;
let json = self.0.into_inner();

View file

@ -12,10 +12,22 @@ 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 },
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 {
@ -38,7 +50,11 @@ impl Display for UnserializerError {
),
Self::Custom(s) => write!(f, "Custom serde error: {}", s),
Self::UnsupportedType { ty, supported } => {
write!(f, "Unsupported type: `{}`, supported type(s): `{}`", ty, supported)
write!(
f,
"Unsupported type: `{}`, supported type(s): `{}`",
ty, supported
)
}
Self::UnexpectedVariant { name, expected } => write!(
f,
@ -74,8 +90,10 @@ fn test() {
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]) };
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());

View file

@ -13,13 +13,19 @@ use crate::{
};
pub(crate) enum InputFileUnserializer {
Memory { file_name: String, data: Cow<'static, [u8]> },
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(&[]) }
Self::Memory {
file_name: String::new(),
data: Cow::Borrowed(&[]),
}
}
}
@ -73,7 +79,10 @@ impl Serializer for InputFileUnserializer {
len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
if name != "InputFile" {
return Err(UnserializerError::UnsupportedType { ty: name, supported: "InputFile" });
return Err(UnserializerError::UnsupportedType {
ty: name,
supported: "InputFile",
});
}
if variant != "Memory" {

View file

@ -20,7 +20,10 @@ impl BotCommand {
S1: Into<String>,
S2: Into<String>,
{
Self { command: command.into(), description: description.into() }
Self {
command: command.into(),
description: description.into(),
}
}
pub fn command<S>(mut self, val: S) -> Self

View file

@ -26,7 +26,11 @@ pub struct Chat {
impl Chat {
pub fn new(id: i64, kind: ChatKind) -> Self {
Self { id, kind, photo: None }
Self {
id,
kind,
photo: None,
}
}
pub fn id(mut self, val: i64) -> Self {
@ -89,7 +93,13 @@ pub struct ChatPublic {
impl ChatPublic {
pub fn new(kind: PublicChatKind) -> Self {
Self { title: None, kind, description: None, invite_link: None, pinned_message: None }
Self {
title: None,
kind,
description: None,
invite_link: None,
pinned_message: None,
}
}
pub fn title<S>(mut self, val: S) -> Self
@ -261,7 +271,10 @@ impl<'de> serde::de::Visitor<'de> for PrivateChatKindVisitor {
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
match v {
"private" => Ok(()),
_ => Err(E::invalid_value(serde::de::Unexpected::Str(v), &r#""private""#)),
_ => Err(E::invalid_value(
serde::de::Unexpected::Str(v),
&r#""private""#,
)),
}
}
}
@ -278,16 +291,31 @@ impl Chat {
matches!(self.kind, ChatKind::Private(_))
}
pub fn is_group(&self) -> bool {
matches!(self.kind, ChatKind::Public(ChatPublic { kind: PublicChatKind::Group(_), .. }))
matches!(
self.kind,
ChatKind::Public(ChatPublic {
kind: PublicChatKind::Group(_),
..
})
)
}
pub fn is_supergroup(&self) -> bool {
matches!(
self.kind,
ChatKind::Public(ChatPublic { kind: PublicChatKind::Supergroup(_), .. })
ChatKind::Public(ChatPublic {
kind: PublicChatKind::Supergroup(_),
..
})
)
}
pub fn is_channel(&self) -> bool {
matches!(self.kind, ChatKind::Public(ChatPublic { kind: PublicChatKind::Channel(_), .. }))
matches!(
self.kind,
ChatKind::Public(ChatPublic {
kind: PublicChatKind::Channel(_),
..
})
)
}
pub fn is_chat(&self) -> bool {

View file

@ -163,7 +163,9 @@ impl ChatMemberKind {
/// Getter for [`Administrator::can_change_info`] field.
pub fn can_change_info(&self) -> Option<bool> {
match &self {
Self::Administrator(Administrator { can_change_info, .. }) => Some(*can_change_info),
Self::Administrator(Administrator {
can_change_info, ..
}) => Some(*can_change_info),
Self::Creator(_)
| Self::Member
| Self::Restricted(_)
@ -175,7 +177,9 @@ impl ChatMemberKind {
/// Getter for [`Administrator::can_post_messages`] field.
pub fn can_post_messages(&self) -> Option<bool> {
match &self {
Self::Administrator(Administrator { can_post_messages, .. }) => *can_post_messages,
Self::Administrator(Administrator {
can_post_messages, ..
}) => *can_post_messages,
Self::Creator(_)
| Self::Member
| Self::Restricted(_)
@ -187,7 +191,9 @@ impl ChatMemberKind {
/// Getter for [`Administrator::can_edit_messages`] field.
pub fn can_edit_messages(&self) -> Option<bool> {
match &self {
Self::Administrator(Administrator { can_edit_messages, .. }) => *can_edit_messages,
Self::Administrator(Administrator {
can_edit_messages, ..
}) => *can_edit_messages,
Self::Creator(_)
| Self::Member
| Self::Restricted(_)
@ -199,9 +205,10 @@ impl ChatMemberKind {
/// Getter for [`Administrator::can_delete_messages`] field.
pub fn can_delete_messages(&self) -> Option<bool> {
match &self {
Self::Administrator(Administrator { can_delete_messages, .. }) => {
Some(*can_delete_messages)
}
Self::Administrator(Administrator {
can_delete_messages,
..
}) => Some(*can_delete_messages),
Self::Creator(_)
| Self::Member
| Self::Restricted(_)
@ -213,7 +220,9 @@ impl ChatMemberKind {
/// Getter for [`Administrator::can_invite_users`] field.
pub fn can_invite_users(&self) -> Option<bool> {
match &self {
Self::Administrator(Administrator { can_invite_users, .. }) => Some(*can_invite_users),
Self::Administrator(Administrator {
can_invite_users, ..
}) => Some(*can_invite_users),
Self::Creator(_)
| Self::Member
| Self::Restricted(_)
@ -225,9 +234,10 @@ impl ChatMemberKind {
/// Getter for [`Administrator::can_restrict_members`] field.
pub fn can_restrict_members(&self) -> Option<bool> {
match &self {
Self::Administrator(Administrator { can_restrict_members, .. }) => {
Some(*can_restrict_members)
}
Self::Administrator(Administrator {
can_restrict_members,
..
}) => Some(*can_restrict_members),
Self::Creator(_)
| Self::Member
| Self::Restricted(_)
@ -239,7 +249,9 @@ impl ChatMemberKind {
/// Getter for [`Administrator::can_pin_messages`] field.
pub fn can_pin_messages(&self) -> Option<bool> {
match &self {
Self::Administrator(Administrator { can_pin_messages, .. }) => *can_pin_messages,
Self::Administrator(Administrator {
can_pin_messages, ..
}) => *can_pin_messages,
Self::Creator(_)
| Self::Member
| Self::Restricted(_)
@ -251,9 +263,10 @@ impl ChatMemberKind {
/// Getter for [`Administrator::can_promote_members`] field.
pub fn can_promote_members(&self) -> Option<bool> {
match &self {
Self::Administrator(Administrator { can_promote_members, .. }) => {
Some(*can_promote_members)
}
Self::Administrator(Administrator {
can_promote_members,
..
}) => Some(*can_promote_members),
Self::Creator(_)
| Self::Member
| Self::Restricted(_)
@ -265,7 +278,9 @@ impl ChatMemberKind {
/// Getter for [`Restricted::can_send_messages`] field.
pub fn can_send_messages(&self) -> Option<bool> {
match &self {
Self::Restricted(Restricted { can_send_messages, .. }) => Some(*can_send_messages),
Self::Restricted(Restricted {
can_send_messages, ..
}) => Some(*can_send_messages),
Self::Creator(_)
| Self::Administrator(_)
| Self::Member
@ -277,9 +292,10 @@ impl ChatMemberKind {
/// Getter for [`Restricted::can_send_media_messages`] field.
pub fn can_send_media_messages(&self) -> Option<bool> {
match &self {
Self::Restricted(Restricted { can_send_media_messages, .. }) => {
Some(*can_send_media_messages)
}
Self::Restricted(Restricted {
can_send_media_messages,
..
}) => Some(*can_send_media_messages),
Self::Creator(_)
| Self::Administrator(_)
| Self::Member
@ -291,9 +307,10 @@ impl ChatMemberKind {
/// Getter for [`Restricted::can_send_other_messages`] field.
pub fn can_send_other_messages(&self) -> Option<bool> {
match &self {
Self::Restricted(Restricted { can_send_other_messages, .. }) => {
Some(*can_send_other_messages)
}
Self::Restricted(Restricted {
can_send_other_messages,
..
}) => Some(*can_send_other_messages),
Self::Creator(_)
| Self::Administrator(_)
| Self::Member
@ -305,9 +322,10 @@ impl ChatMemberKind {
/// Getter for [`Restricted::can_add_web_page_previews`] field.
pub fn can_add_web_page_previews(&self) -> Option<bool> {
match &self {
Self::Restricted(Restricted { can_add_web_page_previews, .. }) => {
Some(*can_add_web_page_previews)
}
Self::Restricted(Restricted {
can_add_web_page_previews,
..
}) => Some(*can_add_web_page_previews),
Self::Creator(_)
| Self::Administrator(_)
| Self::Member

View file

@ -37,7 +37,11 @@ impl EncryptedCredentials {
S2: Into<String>,
S3: Into<String>,
{
Self { data: data.into(), hash: hash.into(), secret: secret.into() }
Self {
data: data.into(),
hash: hash.into(),
secret: secret.into(),
}
}
pub fn data<S>(mut self, val: S) -> Self

View file

@ -24,7 +24,10 @@ impl EncryptedPassportElement {
where
S: Into<String>,
{
Self { hash: hash.into(), kind }
Self {
hash: hash.into(),
kind,
}
}
pub fn hash<S>(mut self, val: S) -> Self
@ -140,7 +143,12 @@ impl EncryptedPassportElementPassport {
where
S: Into<String>,
{
Self { data: data.into(), front_side, selfie, translation: None }
Self {
data: data.into(),
front_side,
selfie,
translation: None,
}
}
pub fn data<S>(mut self, val: S) -> Self
@ -233,7 +241,13 @@ impl EncryptedPassportElementDriverLicense {
where
S: Into<String>,
{
Self { data: data.into(), front_side, reverse_side, selfie, translation: None }
Self {
data: data.into(),
front_side,
reverse_side,
selfie,
translation: None,
}
}
pub fn data<S>(mut self, val: S) -> Self
@ -330,7 +344,13 @@ impl EncryptedPassportElementIdentityCard {
where
S: Into<String>,
{
Self { data: data.into(), front_side, reverse_side, selfie, translation: None }
Self {
data: data.into(),
front_side,
reverse_side,
selfie,
translation: None,
}
}
pub fn data<S>(mut self, val: S) -> Self
@ -413,7 +433,12 @@ impl EncryptedPassportElementInternalPassport {
where
S: Into<String>,
{
Self { data: data.into(), front_side, selfie, translation: None }
Self {
data: data.into(),
front_side,
selfie,
translation: None,
}
}
pub fn data<S>(mut self, val: S) -> Self
@ -505,7 +530,10 @@ impl EncryptedPassportElementUtilityBill {
where
F: Into<Vec<PassportFile>>,
{
Self { files: files.into(), translation: None }
Self {
files: files.into(),
translation: None,
}
}
pub fn files<P>(mut self, val: P) -> Self
@ -556,7 +584,10 @@ impl EncryptedPassportElementBankStatement {
where
F: Into<Vec<PassportFile>>,
{
Self { files: files.into(), translation: None }
Self {
files: files.into(),
translation: None,
}
}
pub fn files<P>(mut self, val: P) -> Self
@ -607,7 +638,10 @@ impl EncryptedPassportElementRentalAgreement {
where
F: Into<Vec<PassportFile>>,
{
Self { files: files.into(), translation: None }
Self {
files: files.into(),
translation: None,
}
}
pub fn files<P>(mut self, val: P) -> Self
@ -658,7 +692,10 @@ impl EncryptedPassportElementPassportRegistration {
where
F: Into<Vec<PassportFile>>,
{
Self { files: files.into(), translation: None }
Self {
files: files.into(),
translation: None,
}
}
pub fn files<P>(mut self, val: P) -> Self
@ -709,7 +746,10 @@ impl EncryptedPassportElementTemporaryRegistration {
where
F: Into<Vec<PassportFile>>,
{
Self { files: files.into(), translation: None }
Self {
files: files.into(),
translation: None,
}
}
pub fn files<P>(mut self, val: P) -> Self
@ -742,7 +782,9 @@ impl EncryptedPassportElementPhoneNumber {
where
S: Into<String>,
{
Self { phone_number: phone_number.into() }
Self {
phone_number: phone_number.into(),
}
}
pub fn phone_number<S>(mut self, val: S) -> Self
@ -766,7 +808,9 @@ impl EncryptedPassportElementEmail {
where
S: Into<String>,
{
Self { email: email.into() }
Self {
email: email.into(),
}
}
pub fn email<S>(mut self, val: S) -> Self

View file

@ -20,8 +20,6 @@ pub struct Game {
/// Photo that will be displayed in the game message in chats.
pub photo: Vec<PhotoSize>,
// FIXME(waffle): SetGameScore method is missing for some reason O_o
#[allow(broken_intra_doc_links)]
/// Brief description of the game or high scores included in the game
/// message. Can be automatically edited to include current high scores
/// for the game when the bot calls [`SetGameScore`], or manually

View file

@ -19,7 +19,11 @@ pub struct GameHighScore {
impl GameHighScore {
pub fn new(position: u32, user: User, score: u32) -> Self {
Self { position, user, score }
Self {
position,
user,
score,
}
}
pub fn position(mut self, val: u32) -> Self {

View file

@ -18,7 +18,10 @@ impl InlineKeyboardButton {
where
S: Into<String>,
{
Self { text: text.into(), kind }
Self {
text: text.into(),
kind,
}
}
pub fn text<S>(mut self, val: S) -> Self
@ -105,11 +108,17 @@ pub enum InlineKeyboardButtonKind {
/// ```
impl InlineKeyboardButton {
pub fn url(text: String, url: String) -> InlineKeyboardButton {
InlineKeyboardButton { text, kind: InlineKeyboardButtonKind::Url(url) }
InlineKeyboardButton {
text,
kind: InlineKeyboardButtonKind::Url(url),
}
}
pub fn callback(text: String, callback_data: String) -> InlineKeyboardButton {
InlineKeyboardButton { text, kind: InlineKeyboardButtonKind::CallbackData(callback_data) }
InlineKeyboardButton {
text,
kind: InlineKeyboardButtonKind::CallbackData(callback_data),
}
}
pub fn switch_inline_query(text: String, switch_inline_query: String) -> InlineKeyboardButton {

View file

@ -35,7 +35,9 @@ impl InlineKeyboardMarkup {
I1: Into<Vec<I2>>,
I2: Into<Vec<InlineKeyboardButton>>,
{
Self { inline_keyboard: inline_keyboard.into().into_iter().map(Into::into).collect() }
Self {
inline_keyboard: inline_keyboard.into().into_iter().map(Into::into).collect(),
}
}
pub fn inline_keyboard<I1, I2>(mut self, val: I1) -> Self
@ -73,7 +75,9 @@ mod tests {
let markup =
InlineKeyboardMarkup::default().append_row(vec![button1.clone(), button2.clone()]);
let expected = InlineKeyboardMarkup { inline_keyboard: vec![vec![button1, button2]] };
let expected = InlineKeyboardMarkup {
inline_keyboard: vec![vec![button1, button2]],
};
assert_eq!(markup, expected);
}
@ -87,7 +91,9 @@ mod tests {
.append_row(vec![button1.clone()])
.append_to_row(button2.clone(), 0);
let expected = InlineKeyboardMarkup { inline_keyboard: vec![vec![button1, button2]] };
let expected = InlineKeyboardMarkup {
inline_keyboard: vec![vec![button1, button2]],
};
assert_eq!(markup, expected);
}
@ -101,7 +107,9 @@ mod tests {
.append_row(vec![button1.clone()])
.append_to_row(button2.clone(), 1);
let expected = InlineKeyboardMarkup { inline_keyboard: vec![vec![button1], vec![button2]] };
let expected = InlineKeyboardMarkup {
inline_keyboard: vec![vec![button1], vec![button2]],
};
assert_eq!(markup, expected);
}

View file

@ -34,7 +34,13 @@ impl InlineQuery {
S2: Into<String>,
S3: Into<String>,
{
Self { id: id.into(), from, location: None, query: query.into(), offset: offset.into() }
Self {
id: id.into(),
from,
location: None,
query: query.into(),
offset: offset.into(),
}
}
pub fn id<S>(mut self, val: S) -> Self

View file

@ -28,7 +28,11 @@ impl InlineQueryResultGame {
S1: Into<String>,
S2: Into<String>,
{
Self { id: id.into(), game_short_name: game_short_name.into(), reply_markup: None }
Self {
id: id.into(),
game_short_name: game_short_name.into(),
reply_markup: None,
}
}
pub fn id<S>(mut self, val: S) -> Self

View file

@ -8,7 +8,10 @@ use std::{borrow::Cow, path::PathBuf};
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum InputFile {
File(PathBuf),
Memory { file_name: String, data: Cow<'static, [u8]> },
Memory {
file_name: String,
data: Cow<'static, [u8]>,
},
Url(String),
FileId(String),
}
@ -26,7 +29,10 @@ impl InputFile {
S: Into<String>,
D: Into<Cow<'static, [u8]>>,
{
Self::Memory { file_name: file_name.into(), data: data.into() }
Self::Memory {
file_name: file_name.into(),
data: data.into(),
}
}
pub fn url<T>(url: T) -> Self
@ -100,7 +106,11 @@ impl InputFile {
match self {
Self::File(path_to_file) => {
let file_name = path_to_file.file_name().unwrap().to_string_lossy().into_owned();
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);

View file

@ -39,7 +39,11 @@ pub struct InputMediaPhoto {
impl InputMediaPhoto {
pub fn new(media: InputFile) -> Self {
Self { media, caption: None, parse_mode: None }
Self {
media,
caption: None,
parse_mode: None,
}
}
pub fn media(mut self, val: InputFile) -> Self {
@ -374,7 +378,12 @@ pub struct InputMediaDocument {
impl InputMediaDocument {
pub fn new(media: InputFile) -> Self {
Self { media, thumb: None, caption: None, parse_mode: None }
Self {
media,
thumb: None,
caption: None,
parse_mode: None,
}
}
pub fn thumb(mut self, val: InputFile) -> Self {

View file

@ -39,7 +39,11 @@ impl InputMessageContentText {
where
S: Into<String>,
{
Self { message_text: message_text.into(), parse_mode: None, disable_web_page_preview: None }
Self {
message_text: message_text.into(),
parse_mode: None,
disable_web_page_preview: None,
}
}
pub fn message_text<S>(mut self, val: S) -> Self
@ -79,7 +83,11 @@ pub struct InputMessageContentLocation {
impl InputMessageContentLocation {
pub fn new(latitude: f64, longitude: f64) -> Self {
Self { latitude, longitude, live_period: None }
Self {
latitude,
longitude,
live_period: None,
}
}
pub fn latitude(mut self, val: f64) -> Self {

View file

@ -29,7 +29,10 @@ impl KeyboardButton {
where
T: Into<String>,
{
Self { text: text.into(), request: None }
Self {
text: text.into(),
request: None,
}
}
pub fn request<T>(mut self, val: T) -> Self
@ -77,15 +80,24 @@ impl<'de> Deserialize<'de> for ButtonRequest {
{
let raw = RawRequest::deserialize(deserializer)?;
match raw {
RawRequest { contact: Some(_), location: Some(_), poll: Some(_) } => {
Err(D::Error::custom(
"`request_contact` and `request_location` fields are mutually exclusive, but \
both were provided",
))
}
RawRequest { contact: Some(_), .. } => Ok(Self::Contact),
RawRequest { location: Some(_), .. } => Ok(Self::Location),
RawRequest { poll: Some(poll_type), .. } => Ok(Self::KeyboardButtonPollType(poll_type)),
RawRequest {
contact: Some(_),
location: Some(_),
poll: Some(_),
} => Err(D::Error::custom(
"`request_contact` and `request_location` fields are mutually exclusive, but both \
were provided",
)),
RawRequest {
contact: Some(_), ..
} => Ok(Self::Contact),
RawRequest {
location: Some(_), ..
} => Ok(Self::Location),
RawRequest {
poll: Some(poll_type),
..
} => Ok(Self::KeyboardButtonPollType(poll_type)),
_ => Err(D::Error::custom(
"Either one of `request_contact` and `request_location` fields is required",
)),
@ -99,16 +111,24 @@ impl Serialize for ButtonRequest {
S: Serializer,
{
match self {
Self::Contact => {
RawRequest { contact: Some(True), location: None, poll: None }.serialize(serializer)
Self::Contact => RawRequest {
contact: Some(True),
location: None,
poll: None,
}
Self::Location => {
RawRequest { contact: None, location: Some(True), poll: None }.serialize(serializer)
.serialize(serializer),
Self::Location => RawRequest {
contact: None,
location: Some(True),
poll: None,
}
Self::KeyboardButtonPollType(poll_type) => {
RawRequest { contact: None, location: None, poll: Some(poll_type.clone()) }
.serialize(serializer)
.serialize(serializer),
Self::KeyboardButtonPollType(poll_type) => RawRequest {
contact: None,
location: None,
poll: Some(poll_type.clone()),
}
.serialize(serializer),
}
}
}
@ -119,7 +139,10 @@ mod tests {
#[test]
fn serialize_no_request() {
let button = KeyboardButton { text: String::from(""), request: None };
let button = KeyboardButton {
text: String::from(""),
request: None,
};
let expected = r#"{"text":""}"#;
let actual = serde_json::to_string(&button).unwrap();
assert_eq!(expected, actual);
@ -127,8 +150,10 @@ mod tests {
#[test]
fn serialize_request_contact() {
let button =
KeyboardButton { text: String::from(""), request: Some(ButtonRequest::Contact) };
let button = KeyboardButton {
text: String::from(""),
request: Some(ButtonRequest::Contact),
};
let expected = r#"{"text":"","request_contact":true}"#;
let actual = serde_json::to_string(&button).unwrap();
assert_eq!(expected, actual);
@ -137,7 +162,10 @@ mod tests {
#[test]
fn deserialize_no_request() {
let json = r#"{"text":""}"#;
let expected = KeyboardButton { text: String::from(""), request: None };
let expected = KeyboardButton {
text: String::from(""),
request: None,
};
let actual = serde_json::from_str(json).unwrap();
assert_eq!(expected, actual);
}
@ -145,8 +173,10 @@ mod tests {
#[test]
fn deserialize_request_contact() {
let json = r#"{"text":"","request_contact":true}"#;
let expected =
KeyboardButton { text: String::from(""), request: Some(ButtonRequest::Contact) };
let expected = KeyboardButton {
text: String::from(""),
request: Some(ButtonRequest::Contact),
};
let actual = serde_json::from_str(json).unwrap();
assert_eq!(expected, actual);
}

View file

@ -10,7 +10,9 @@ impl KeyboardButtonPollType {
where
S: Into<String>,
{
Self { poll_type: poll_type.into() }
Self {
poll_type: poll_type.into(),
}
}
pub fn poll_type<S>(mut self, val: S) -> Self

View file

@ -24,7 +24,10 @@ impl LabeledPrice {
where
S: Into<String>,
{
Self { label: label.into(), amount }
Self {
label: label.into(),
amount,
}
}
pub fn label<S>(mut self, val: S) -> Self
@ -47,7 +50,10 @@ mod tests {
#[test]
fn serialize() {
let labeled_price = LabeledPrice { label: "Label".to_string(), amount: 60 };
let labeled_price = LabeledPrice {
label: "Label".to_string(),
amount: 60,
};
let expected = r#"{"label":"Label","amount":60}"#;
let actual = serde_json::to_string(&labeled_price).unwrap();
assert_eq!(actual, expected);

View file

@ -12,7 +12,10 @@ pub struct Location {
impl Location {
pub fn new(longitude: f64, latitude: f64) -> Self {
Self { longitude, latitude }
Self {
longitude,
latitude,
}
}
pub fn latitude(mut self, val: f64) -> Self {

View file

@ -29,7 +29,12 @@ impl MaskPosition {
where
S: Into<String>,
{
Self { point: point.into(), x_shift, y_shift, scale }
Self {
point: point.into(),
x_shift,
y_shift,
scale,
}
}
pub fn point<S>(mut self, val: S) -> Self

View file

@ -28,7 +28,12 @@ impl Me {
can_read_all_group_messages: bool,
supports_inline_queries: bool,
) -> Self {
Self { user, can_join_groups, can_read_all_group_messages, supports_inline_queries }
Self {
user,
can_join_groups,
can_read_all_group_messages,
supports_inline_queries,
}
}
pub fn user(mut self, val: User) -> Self {

View file

@ -33,7 +33,13 @@ pub struct Message {
impl Message {
pub fn new(id: i32, date: i32, chat: Chat, kind: MessageKind) -> Self {
Self { id, date, chat, kind, via_bot: None }
Self {
id,
date,
chat,
kind,
via_bot: None,
}
}
pub fn id(mut self, val: i32) -> Self {
@ -105,7 +111,13 @@ pub struct MessageCommon {
impl MessageCommon {
pub fn new(forward_kind: ForwardKind, media_kind: MediaKind) -> Self {
Self { from: None, forward_kind, edit_date: None, media_kind, reply_markup: None }
Self {
from: None,
forward_kind,
edit_date: None,
media_kind,
reply_markup: None,
}
}
pub fn from(mut self, val: User) -> Self {
@ -147,7 +159,9 @@ impl MessageNewChatMembers {
where
N: Into<Vec<User>>,
{
Self { new_chat_members: new_chat_members.into() }
Self {
new_chat_members: new_chat_members.into(),
}
}
pub fn new_chat_members<N>(mut self, val: N) -> Self
@ -171,7 +185,9 @@ impl MessageLeftChatMember {
where
N: Into<User>,
{
Self { left_chat_member: left_chat_member.into() }
Self {
left_chat_member: left_chat_member.into(),
}
}
pub fn left_chat_member<N>(mut self, val: N) -> Self
@ -194,7 +210,9 @@ impl MessageNewChatTitle {
where
N: Into<String>,
{
Self { new_chat_title: new_chat_title.into() }
Self {
new_chat_title: new_chat_title.into(),
}
}
pub fn new_chat_title<N>(mut self, val: N) -> Self
@ -217,7 +235,9 @@ impl MessageNewChatPhoto {
where
N: Into<Vec<PhotoSize>>,
{
Self { new_chat_photo: new_chat_photo.into() }
Self {
new_chat_photo: new_chat_photo.into(),
}
}
pub fn new_chat_photo<N>(mut self, val: N) -> Self
@ -306,7 +326,10 @@ pub struct MessageMigrate {
impl MessageMigrate {
pub fn new(migrate_to_chat_id: i64, migrate_from_chat_id: i64) -> Self {
Self { migrate_to_chat_id, migrate_from_chat_id }
Self {
migrate_to_chat_id,
migrate_from_chat_id,
}
}
pub fn migrate_to_chat_id(mut self, val: i64) -> Self {
@ -331,7 +354,9 @@ pub struct MessagePinned {
impl MessagePinned {
pub fn new(pinned: Message) -> Self {
Self { pinned: Box::new(pinned) }
Self {
pinned: Box::new(pinned),
}
}
pub fn pinned(mut self, val: Message) -> Self {
@ -395,7 +420,9 @@ impl MessageConnectedWebsite {
where
S: Into<String>,
{
Self { connected_website: connected_website.into() }
Self {
connected_website: connected_website.into(),
}
}
pub fn connected_website<S>(mut self, val: S) -> Self
@ -457,7 +484,12 @@ pub struct ForwardChannel {
impl ForwardChannel {
pub fn new(date: i32, chat: Chat, message_id: i32) -> Self {
Self { date, chat, message_id, signature: None }
Self {
date,
chat,
message_id,
signature: None,
}
}
pub fn date(mut self, val: i32) -> Self {
@ -570,7 +602,12 @@ impl MediaAnimation {
where
CE: Into<Vec<MessageEntity>>,
{
Self { animation, document: (), caption: None, caption_entities: caption_entities.into() }
Self {
animation,
document: (),
caption: None,
caption_entities: caption_entities.into(),
}
}
pub fn animation(mut self, val: Animation) -> Self {
@ -615,7 +652,11 @@ impl MediaAudio {
where
CE: Into<Vec<MessageEntity>>,
{
Self { audio, caption: None, caption_entities: caption_entities.into() }
Self {
audio,
caption: None,
caption_entities: caption_entities.into(),
}
}
pub fn audio(mut self, val: Audio) -> Self {
@ -677,7 +718,11 @@ impl MediaDocument {
where
CE: Into<Vec<MessageEntity>>,
{
Self { document, caption: None, caption_entities: caption_entities.into() }
Self {
document,
caption: None,
caption_entities: caption_entities.into(),
}
}
pub fn document(mut self, val: Document) -> Self {
@ -857,7 +902,10 @@ impl MediaText {
S: Into<String>,
E: Into<Vec<MessageEntity>>,
{
Self { text: text.into(), entities: entities.into() }
Self {
text: text.into(),
entities: entities.into(),
}
}
pub fn text<S>(mut self, val: S) -> Self
@ -978,7 +1026,11 @@ impl MediaVoice {
where
CE: Into<Vec<MessageEntity>>,
{
Self { voice, caption: None, caption_entities: caption_entities.into() }
Self {
voice,
caption: None,
caption_entities: caption_entities.into(),
}
}
pub fn voice(mut self, val: Voice) -> Self {
@ -1125,7 +1177,10 @@ mod getters {
pub fn reply_to_message(&self) -> Option<&Message> {
match &self.kind {
Common(MessageCommon {
forward_kind: ForwardKind::Origin(ForwardOrigin { reply_to_message, .. }),
forward_kind:
ForwardKind::Origin(ForwardOrigin {
reply_to_message, ..
}),
..
}) => reply_to_message.as_ref().map(Deref::deref),
_ => None,
@ -1180,27 +1235,45 @@ mod getters {
pub fn caption_entities(&self) -> Option<&[MessageEntity]> {
match &self.kind {
Common(MessageCommon {
media_kind: MediaKind::Animation(MediaAnimation { caption_entities, .. }),
media_kind:
MediaKind::Animation(MediaAnimation {
caption_entities, ..
}),
..
})
| Common(MessageCommon {
media_kind: MediaKind::Audio(MediaAudio { caption_entities, .. }),
media_kind:
MediaKind::Audio(MediaAudio {
caption_entities, ..
}),
..
})
| Common(MessageCommon {
media_kind: MediaKind::Document(MediaDocument { caption_entities, .. }),
media_kind:
MediaKind::Document(MediaDocument {
caption_entities, ..
}),
..
})
| Common(MessageCommon {
media_kind: MediaKind::Photo(MediaPhoto { caption_entities, .. }),
media_kind:
MediaKind::Photo(MediaPhoto {
caption_entities, ..
}),
..
})
| Common(MessageCommon {
media_kind: MediaKind::Video(MediaVideo { caption_entities, .. }),
media_kind:
MediaKind::Video(MediaVideo {
caption_entities, ..
}),
..
})
| Common(MessageCommon {
media_kind: MediaKind::Voice(MediaVoice { caption_entities, .. }),
media_kind:
MediaKind::Voice(MediaVoice {
caption_entities, ..
}),
..
}) => Some(caption_entities),
_ => None,
@ -1408,32 +1481,37 @@ mod getters {
pub fn super_group_chat_created(&self) -> Option<True> {
match &self.kind {
SupergroupChatCreated(MessageSupergroupChatCreated { supergroup_chat_created }) => {
Some(*supergroup_chat_created)
}
SupergroupChatCreated(MessageSupergroupChatCreated {
supergroup_chat_created,
}) => Some(*supergroup_chat_created),
_ => None,
}
}
pub fn channel_chat_created(&self) -> Option<True> {
match &self.kind {
ChannelChatCreated(MessageChannelChatCreated { channel_chat_created }) => {
Some(*channel_chat_created)
}
ChannelChatCreated(MessageChannelChatCreated {
channel_chat_created,
}) => Some(*channel_chat_created),
_ => None,
}
}
pub fn migrate_to_chat_id(&self) -> Option<i64> {
match &self.kind {
Migrate(MessageMigrate { migrate_to_chat_id, .. }) => Some(*migrate_to_chat_id),
Migrate(MessageMigrate {
migrate_to_chat_id, ..
}) => Some(*migrate_to_chat_id),
_ => None,
}
}
pub fn migrate_from_chat_id(&self) -> Option<i64> {
match &self.kind {
Migrate(MessageMigrate { migrate_from_chat_id, .. }) => Some(*migrate_from_chat_id),
Migrate(MessageMigrate {
migrate_from_chat_id,
..
}) => Some(*migrate_from_chat_id),
_ => None,
}
}
@ -1490,13 +1568,17 @@ impl Message {
pub fn url(&self) -> Option<reqwest::Url> {
match &self.chat.kind {
ChatKind::Public(ChatPublic {
kind: PublicChatKind::Channel(PublicChatChannel { username: Some(username) }),
kind:
PublicChatKind::Channel(PublicChatChannel {
username: Some(username),
}),
..
})
| ChatKind::Public(ChatPublic {
kind:
PublicChatKind::Supergroup(PublicChatSupergroup {
username: Some(username), ..
username: Some(username),
..
}),
..
}) => Some(

View file

@ -21,7 +21,11 @@ pub struct MessageEntity {
impl MessageEntity {
pub fn new(kind: MessageEntityKind, offset: usize, length: usize) -> Self {
Self { kind, offset, length }
Self {
kind,
offset,
length,
}
}
pub fn kind(mut self, val: MessageEntityKind) -> Self {
@ -82,7 +86,9 @@ mod tests {
assert_eq!(
MessageEntity {
kind: MessageEntityKind::TextLink { url: "ya.ru".into() },
kind: MessageEntityKind::TextLink {
url: "ya.ru".into()
},
offset: 1,
length: 2,
},
@ -99,7 +105,9 @@ mod tests {
assert_eq!(
MessageEntity {
kind: MessageEntityKind::Pre { language: Some("rust".to_string()) },
kind: MessageEntityKind::Pre {
language: Some("rust".to_string())
},
offset: 1,
length: 2,
},
@ -143,7 +151,9 @@ mod tests {
username: None,
language_code: None,
}),
forward_kind: ForwardKind::Origin(ForwardOrigin { reply_to_message: None }),
forward_kind: ForwardKind::Origin(ForwardOrigin {
reply_to_message: None,
}),
edit_date: None,
media_kind: MediaKind::Text(MediaText {
text: "no yes no".to_string(),

View file

@ -10,7 +10,11 @@ pub struct NonStrictVec<T>(pub Vec<Result<T, (serde_json::Value, serde_json::Err
impl<T: DeserializeOwned> From<Vec<serde_json::Value>> for NonStrictVec<T> {
fn from(vec: Vec<Value>) -> Self {
Self(vec.into_iter().map(|val| from_value(val.clone()).map_err(|e| (val, e))).collect())
Self(
vec.into_iter()
.map(|val| from_value(val.clone()).map_err(|e| (val, e)))
.collect(),
)
}
}

View file

@ -21,7 +21,10 @@ impl PassportData {
where
E: Into<Vec<EncryptedPassportElement>>,
{
Self { data: data.into(), credentials }
Self {
data: data.into(),
credentials,
}
}
pub fn data<E>(mut self, val: E) -> Self

View file

@ -18,7 +18,10 @@ impl PassportElementError {
where
S: Into<String>,
{
Self { message: message.into(), kind }
Self {
message: message.into(),
kind,
}
}
pub fn message<S>(mut self, val: S) -> Self
@ -94,7 +97,11 @@ impl PassportElementErrorDataField {
S1: Into<String>,
S2: Into<String>,
{
Self { r#type, field_name: field_name.into(), data_hash: data_hash.into() }
Self {
r#type,
field_name: field_name.into(),
data_hash: data_hash.into(),
}
}
pub fn r#type(mut self, val: PassportElementErrorDataFieldType) -> Self {
@ -140,7 +147,10 @@ impl PassportElementErrorFrontSide {
where
S: Into<String>,
{
Self { r#type, file_hash: file_hash.into() }
Self {
r#type,
file_hash: file_hash.into(),
}
}
pub fn r#type(mut self, val: PassportElementErrorFrontSideType) -> Self {
@ -178,7 +188,10 @@ impl PassportElementErrorReverseSide {
where
S: Into<String>,
{
Self { r#type, file_hash: file_hash.into() }
Self {
r#type,
file_hash: file_hash.into(),
}
}
pub fn r#type(mut self, val: PassportElementErrorReverseSideType) -> Self {
@ -214,7 +227,10 @@ impl PassportElementErrorSelfie {
where
S: Into<String>,
{
Self { r#type, file_hash: file_hash.into() }
Self {
r#type,
file_hash: file_hash.into(),
}
}
pub fn r#type(mut self, val: PassportElementErrorSelfieType) -> Self {
@ -251,7 +267,10 @@ impl PassportElementErrorFile {
where
S: Into<String>,
{
Self { r#type, file_hash: file_hash.into() }
Self {
r#type,
file_hash: file_hash.into(),
}
}
pub fn r#type(mut self, val: PassportElementErrorFileType) -> Self {
@ -288,7 +307,10 @@ impl PassportElementErrorFiles {
where
S: Into<Vec<String>>,
{
Self { r#type, file_hashes: file_hashes.into() }
Self {
r#type,
file_hashes: file_hashes.into(),
}
}
pub fn r#type(mut self, val: PassportElementErrorFilesType) -> Self {
@ -326,7 +348,10 @@ impl PassportElementErrorTranslationFile {
where
S: Into<String>,
{
Self { r#type, file_hash: file_hash.into() }
Self {
r#type,
file_hash: file_hash.into(),
}
}
pub fn r#type(mut self, val: PassportElementErrorTranslationFileType) -> Self {
@ -363,7 +388,10 @@ impl PassportElementErrorTranslationFiles {
where
S: Into<Vec<String>>,
{
Self { r#type, file_hashes: file_hashes.into() }
Self {
r#type,
file_hashes: file_hashes.into(),
}
}
pub fn r#type(mut self, val: PassportElementErrorTranslationFilesType) -> Self {
@ -400,7 +428,10 @@ impl PassportElementErrorUnspecified {
where
S: Into<String>,
{
Self { r#type, element_hash: file_hash.into() }
Self {
r#type,
element_hash: file_hash.into(),
}
}
pub fn r#type(mut self, val: PassportElementErrorUnspecifiedType) -> Self {

View file

@ -187,7 +187,10 @@ impl PollOption {
where
S: Into<String>,
{
Self { text: text.into(), voter_count }
Self {
text: text.into(),
voter_count,
}
}
pub fn text<S>(mut self, val: S) -> Self

View file

@ -21,7 +21,11 @@ impl PollAnswer {
S: Into<String>,
O: Into<Vec<i32>>,
{
Self { poll_id: poll_id.into(), user, option_ids: option_ids.into() }
Self {
poll_id: poll_id.into(),
user,
option_ids: option_ids.into(),
}
}
pub fn poll_id<S>(mut self, val: S) -> Self

View file

@ -24,7 +24,11 @@ impl ShippingOption {
S2: Into<String>,
P: Into<Vec<LabeledPrice>>,
{
Self { id: id.into(), title: title.into(), prices: prices.into() }
Self {
id: id.into(),
title: title.into(),
prices: prices.into(),
}
}
pub fn id<S>(mut self, val: S) -> Self
@ -61,7 +65,10 @@ mod tests {
let shipping_option = ShippingOption {
id: "0".to_string(),
title: "Option".to_string(),
prices: vec![LabeledPrice { label: "Label".to_string(), amount: 60 }],
prices: vec![LabeledPrice {
label: "Label".to_string(),
amount: 60,
}],
};
let expected = r#"{"id":"0","title":"Option","prices":[{"label":"Label","amount":60}]}"#;
let actual = serde_json::to_string(&shipping_option).unwrap();

View file

@ -31,7 +31,12 @@ impl ShippingQuery {
S1: Into<String>,
S2: Into<String>,
{
Self { id: id.into(), from, invoice_payload: invoice_payload.into(), shipping_address }
Self {
id: id.into(),
from,
invoice_payload: invoice_payload.into(),
shipping_address,
}
}
pub fn id<S>(mut self, val: S) -> Self

View file

@ -175,7 +175,9 @@ mod test {
username: Some(String::from("WaffleLapkin")),
language_code: Some(String::from("en")),
}),
forward_kind: ForwardKind::Origin(ForwardOrigin { reply_to_message: None }),
forward_kind: ForwardKind::Origin(ForwardOrigin {
reply_to_message: None,
}),
edit_date: None,
media_kind: MediaKind::Text(MediaText {
text: String::from("hello there"),

View file

@ -20,7 +20,10 @@ impl UserProfilePhotos {
P1: Into<Vec<P2>>,
P2: Into<Vec<PhotoSize>>,
{
Self { total_count, photos: photos.into().into_iter().map(Into::into).collect() }
Self {
total_count,
photos: photos.into().into_iter().map(Into::into).collect(),
}
}
pub fn total_count(mut self, val: u32) -> Self {