mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-24 09:16:12 +01:00
Merge pull request #35 from teloxide/custom_url
Add `Bot::{set_,}api_url` methods
This commit is contained in:
commit
8f3bb9d96b
49 changed files with 782 additions and 242 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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)))),
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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,
|
||||
..
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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" {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue