From 45cf62ce8e0fd1e7892587512b8a9b25c8341f9d Mon Sep 17 00:00:00 2001 From: Waffle Date: Fri, 27 Nov 2020 01:18:57 +0300 Subject: [PATCH] Refactor Bot for a bit - Move default parse mode to an adaptor - Remove bot builder (it's not usefull anymore, since parse_mode is moved away) - Undeprecate bot constructors --- src/adaptors.rs | 4 + src/adaptors/parse_mode.rs | 108 ++++++++++++++++++ src/bot/api.rs | 79 +++---------- src/bot/mod.rs | 205 ++++++++-------------------------- src/lib.rs | 6 +- src/requests/requester_ext.rs | 34 +++++- 6 files changed, 207 insertions(+), 229 deletions(-) create mode 100644 src/adaptors/parse_mode.rs diff --git a/src/adaptors.rs b/src/adaptors.rs index 09c4fec8..40982784 100644 --- a/src/adaptors.rs +++ b/src/adaptors.rs @@ -19,6 +19,8 @@ pub mod cache_me; #[cfg_attr(all(docsrs, feature = "nightly"), doc(cfg(feature = "throttle")))] pub mod throttle; +mod parse_mode; + #[cfg(feature = "auto_send")] #[cfg_attr(all(docsrs, feature = "nightly"), doc(cfg(feature = "auto_send")))] pub use auto_send::AutoSend; @@ -29,4 +31,6 @@ pub use cache_me::CacheMe; #[cfg_attr(all(docsrs, feature = "nightly"), doc(cfg(feature = "throttle")))] pub use throttle::Throttle; +pub use parse_mode::DefaultParseMode; + // FIXME: move default `parse_mode` to adaptor diff --git a/src/adaptors/parse_mode.rs b/src/adaptors/parse_mode.rs new file mode 100644 index 00000000..e191e73d --- /dev/null +++ b/src/adaptors/parse_mode.rs @@ -0,0 +1,108 @@ +use crate::{ + prelude::Requester, + requests::HasPayload, + types::{ChatId, InputFile, ParseMode, *}, +}; + +/// Default parse mode adaptor, see +/// [`RequesterExt::parse_mode`](crate::requests::RequesterExt::parse_mode) +pub struct DefaultParseMode { + bot: B, + mode: ParseMode, +} + +impl DefaultParseMode { + /// Creates new [`DefaultParseMode`] + /// + /// Note: it's recommended to use [`RequesterExt::parse_mode`] instead. + /// + /// [`RequesterExt::parse_mode`]: crate::requests::RequesterExt::parse_mode + pub fn new(bot: B, parse_mode: ParseMode) -> Self { + Self { bot, mode: parse_mode } + } + + /// Allows to access inner bot + pub fn inner(&self) -> &B { + &self.bot + } + + /// Unwraps inner bot + pub fn into_inner(self) -> B { + self.bot + } +} + +macro_rules! f { + ($m:ident $this:ident ($($arg:ident : $T:ty),*)) => { + { + let mut req = $this.inner().$m($($arg),*); + req.payload_mut().parse_mode = Some($this.mode); + req + } + }; +} + +macro_rules! fty { + ($T:ident) => { + B::$T + }; +} + +macro_rules! fid { + ($m:ident $this:ident ($($arg:ident : $T:ty),*)) => { + $this.inner().$m($($arg),*) + }; +} + +impl Requester for DefaultParseMode { + type Err = B::Err; + + requester_forward! { + send_message, + send_photo, + send_video, + send_audio, + send_document, + send_animation, + send_voice, + edit_message_text, + edit_message_text_inline, + edit_message_caption, + edit_message_caption_inline => f, fty + } + + type SendPoll = B::SendPoll; + + fn send_poll( + &self, + chat_id: C, + question: Q, + options: O, + type_: PollType, + ) -> Self::SendPoll + where + C: Into, + Q: Into, + O: IntoIterator, + { + let mut req = self.inner().send_poll(chat_id, question, options, type_); + req.payload_mut().explanation_parse_mode = Some(self.mode); + req + } + + requester_forward! { + get_me, get_updates, set_webhook, delete_webhook, get_webhook_info, + forward_message, send_video_note, send_media_group, send_location, + edit_message_live_location, edit_message_live_location_inline, stop_message_live_location, + stop_message_live_location_inline, send_venue, send_contact, send_dice, + send_chat_action, get_user_profile_photos, get_file, kick_chat_member, unban_chat_member, + restrict_chat_member, promote_chat_member, set_chat_administrator_custom_title, set_chat_permissions, + export_chat_invite_link, set_chat_photo, delete_chat_photo, set_chat_title, set_chat_description, + pin_chat_message, unpin_chat_message, leave_chat, get_chat, get_chat_administrators, get_chat_members_count, + get_chat_member, set_chat_sticker_set, delete_chat_sticker_set, answer_callback_query, set_my_commands, + get_my_commands, answer_inline_query, edit_message_media, edit_message_media_inline, edit_message_reply_markup, + edit_message_reply_markup_inline, stop_poll, delete_message, send_sticker, get_sticker_set, upload_sticker_file, + create_new_sticker_set, add_sticker_to_set, set_sticker_position_in_set, delete_sticker_from_set, + set_sticker_set_thumb, send_invoice, answer_shipping_query, answer_pre_checkout_query, set_passport_data_errors => fid, fty + } +} diff --git a/src/bot/api.rs b/src/bot/api.rs index fa7c5d2d..9bebc7bb 100644 --- a/src/bot/api.rs +++ b/src/bot/api.rs @@ -21,7 +21,7 @@ use crate::{ }, types::{ BotCommand, ChatId, ChatPermissions, InlineQueryResult, InputFile, InputMedia, - InputSticker, LabeledPrice, ParseMode, TargetMessage, + InputSticker, LabeledPrice, TargetMessage, }, Bot, }; @@ -130,10 +130,7 @@ impl Bot { C: Into, T: Into, { - self.with_default_parse_mode_if_specified( - SendMessage::new(self.clone(), chat_id, text), - SendMessage::parse_mode, - ) + SendMessage::new(self.clone(), chat_id, text) } /// Use this method to forward messages of any kind. @@ -192,10 +189,7 @@ impl Bot { where C: Into, { - self.with_default_parse_mode_if_specified( - SendPhoto::new(self.clone(), chat_id, photo), - SendPhoto::parse_mode, - ) + SendPhoto::new(self.clone(), chat_id, photo) } /// @@ -213,10 +207,7 @@ impl Bot { where C: Into, { - self.with_default_parse_mode_if_specified( - SendAudio::new(self.clone(), chat_id, audio), - SendAudio::parse_mode, - ) + SendAudio::new(self.clone(), chat_id, audio) } /// Use this method to send general files. @@ -247,10 +238,7 @@ impl Bot { where C: Into, { - self.with_default_parse_mode_if_specified( - SendDocument::new(self.clone(), chat_id, document), - SendDocument::parse_mode, - ) + SendDocument::new(self.clone(), chat_id, document) } /// Use this method to send video files, Telegram clients support mp4 videos @@ -284,10 +272,7 @@ impl Bot { where C: Into, { - self.with_default_parse_mode_if_specified( - SendVideo::new(self.clone(), chat_id, video), - SendVideo::parse_mode, - ) + SendVideo::new(self.clone(), chat_id, video) } /// Use this method to send animation files (GIF or H.264/MPEG-4 AVC video @@ -312,10 +297,7 @@ impl Bot { where C: Into, { - self.with_default_parse_mode_if_specified( - SendAnimation::new(self.clone(), chat_id, animation), - SendAnimation::parse_mode, - ) + SendAnimation::new(self.clone(), chat_id, animation) } /// Use this method to send audio files, if you want Telegram clients to @@ -355,10 +337,7 @@ impl Bot { where C: Into, { - self.with_default_parse_mode_if_specified( - SendVoice::new(self.clone(), chat_id, voice), - SendVoice::parse_mode, - ) + SendVoice::new(self.clone(), chat_id, voice) } /// As of [v.4.0], Telegram clients support rounded square mp4 videos of up @@ -583,10 +562,7 @@ impl Bot { Q: Into, O: Into>, { - self.with_default_parse_mode_if_specified( - SendPoll::new(self.clone(), chat_id, question, options), - SendPoll::explanation_parse_mode, - ) + SendPoll::new(self.clone(), chat_id, question, options) } /// Use this method when you need to tell the user that something is @@ -1069,12 +1045,7 @@ impl Bot { C: Into, T: Into, { - match self.parse_mode { - None => EditMessageText::new(self.clone(), chat_id, message_id, text), - Some(parse_mode) => { - EditMessageText::new(self.clone(), chat_id, message_id, text).parse_mode(parse_mode) - } - } + EditMessageText::new(self.clone(), chat_id, message_id, text) } /// Use this method to edit text and game messages sent via the bot. @@ -1105,11 +1076,7 @@ impl Bot { I: Into, T: Into, { - match self.parse_mode { - None => EditInlineMessageText::new(self.clone(), inline_message_id, text), - Some(parse_mode) => EditInlineMessageText::new(self.clone(), inline_message_id, text) - .parse_mode(parse_mode), - } + EditInlineMessageText::new(self.clone(), inline_message_id, text) } /// Use this method to edit captions of messages sent via the bot. @@ -1130,12 +1097,7 @@ impl Bot { where C: Into, { - match self.parse_mode { - None => EditMessageCaption::new(self.clone(), chat_id, message_id), - Some(parse_mode) => { - EditMessageCaption::new(self.clone(), chat_id, message_id).parse_mode(parse_mode) - } - } + EditMessageCaption::new(self.clone(), chat_id, message_id) } /// Use this method to edit captions of messages sent via the bot. @@ -1155,11 +1117,7 @@ impl Bot { where I: Into, { - match self.parse_mode { - None => EditInlineMessageCaption::new(self.clone(), inline_message_id), - Some(parse_mode) => EditInlineMessageCaption::new(self.clone(), inline_message_id) - .parse_mode(parse_mode), - } + EditInlineMessageCaption::new(self.clone(), inline_message_id) } /// Use this method to edit animation, audio, document, photo, or video @@ -1691,17 +1649,6 @@ impl Bot { { SetStickerSetThumb::new(self.clone(), name, user_id) } - - fn with_default_parse_mode_if_specified( - &self, - builder: Builder, - f: fn(Builder, ParseMode) -> Builder, - ) -> Builder { - match self.parse_mode { - None => builder, - Some(parse_mode) => f(builder, parse_mode), - } - } } impl Requester for Bot { diff --git a/src/bot/mod.rs b/src/bot/mod.rs index bdba198b..6187cf41 100644 --- a/src/bot/mod.rs +++ b/src/bot/mod.rs @@ -11,7 +11,6 @@ use crate::{ net, requests::{Payload, ResponseResult}, serde_multipart, - types::ParseMode, }; mod api; @@ -31,56 +30,15 @@ pub struct Bot { token: Arc, api_url: ApiUrl, client: Client, - parse_mode: Option, } impl Bot { - /// Creates a new `Bot` with the `TELOXIDE_TOKEN` & `TELOXIDE_PROXY` - /// environmental variables (a bot's token & a proxy) and the default - /// [`reqwest::Client`]. - /// - /// This function passes the value of `TELOXIDE_PROXY` into - /// [`reqwest::Proxy::all`], if it exists, otherwise returns the default - /// client. - /// - /// # Panics - /// - If cannot get the `TELOXIDE_TOKEN` environmental variable. - /// - If it cannot create [`reqwest::Client`]. - /// - /// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html - /// [`reqwest::Proxy::all`]: https://docs.rs/reqwest/latest/reqwest/struct.Proxy.html#method.all - #[must_use] - pub fn from_env() -> Self { - BotBuilder::new().build() - } - - /// Creates a new `Bot` with the `TELOXIDE_TOKEN` environmental variable (a - /// bot's token) and your [`reqwest::Client`]. - /// - /// # Panics - /// If cannot get the `TELOXIDE_TOKEN` environmental variable. - /// - /// # Caution - /// Your custom client might not be configured correctly to be able to work - /// in long time durations, see [issue 223]. - /// - /// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html - /// [issue 223]: https://github.com/teloxide/teloxide/issues/223 - #[deprecated] - #[allow(deprecated)] - pub fn from_env_with_client(client: Client) -> Self { - Self::with_client(&get_env(TELOXIDE_TOKEN), client) - } - /// Creates a new `Bot` with the specified token and the default - /// [`reqwest::Client`]. + /// [http-client](reqwest::Client). /// /// # Panics - /// If it cannot create [`reqwest::Client`]. /// - /// [`reqwest::Client`]: https://docs.rs/reqwest/latest/reqwest/struct.Client.html - #[deprecated] - #[allow(deprecated)] + /// If it cannot create [`reqwest::Client`]. pub fn new(token: S) -> Self where S: Into, @@ -97,8 +55,6 @@ impl Bot { /// /// [`reqwest::Client`]: https://docs.rs/reqwest/latest/reqwest/struct.Client.html /// [issue 223]: https://github.com/teloxide/teloxide/issues/223 - #[deprecated] - #[allow(deprecated)] pub fn with_client(token: S, client: Client) -> Self where S: Into, @@ -107,9 +63,52 @@ impl Bot { token: Into::>::into(Into::::into(token)), api_url: ApiUrl::Default, client, - parse_mode: None, } } + + /// Creates a new `Bot` with the `TELOXIDE_TOKEN` & `TELOXIDE_PROXY` + /// environmental variables (a bot's token & a proxy) and the default + /// [`reqwest::Client`]. + /// + /// This function passes the value of `TELOXIDE_PROXY` into + /// [`reqwest::Proxy::all`], if it exists, otherwise returns the default + /// client. + /// + /// # Panics + /// - If cannot get the `TELOXIDE_TOKEN` environmental variable. + /// - If it cannot create [`reqwest::Client`]. + /// + /// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html + /// [`reqwest::Proxy::all`]: https://docs.rs/reqwest/latest/reqwest/struct.Proxy.html#method.all + pub fn from_env() -> Self { + Self::from_env_with_client(crate::client_from_env()) + } + + /// Creates a new `Bot` with the `TELOXIDE_TOKEN` environmental variable (a + /// bot's token) and your [`reqwest::Client`]. + /// + /// # Panics + /// If cannot get the `TELOXIDE_TOKEN` environmental variable. + /// + /// # Caution + /// Your custom client might not be configured correctly to be able to work + /// in long time durations, see [issue 223]. + /// + /// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html + /// [issue 223]: https://github.com/teloxide/teloxide/issues/223 + pub fn from_env_with_client(client: Client) -> Self { + Self::with_client(&get_env(TELOXIDE_TOKEN), client) + } + + /// Returns currently used token + pub fn token(&self) -> &str { + &self.token + } + + /// Returns currently used http-client + pub fn client(&self) -> &Client { + &self.client + } } impl Bot { @@ -182,115 +181,3 @@ pub(crate) fn build_sound_bot() -> Client { fn get_env(env: &'static str) -> String { std::env::var(env).unwrap_or_else(|_| panic!("Cannot get the {} env variable", env)) } - -impl Bot { - pub fn token(&self) -> &str { - &self.token - } - - pub fn client(&self) -> &Client { - &self.client - } -} - -/// A builder of [`Bot`], supporting some extra settings. -/// -/// [`Bot`] crate::Bot -#[derive(Debug, Default)] -pub struct BotBuilder { - token: Option, - client: Option, - parse_mode: Option, -} - -impl BotBuilder { - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Specifies a custom HTTPS client. Otherwise, the default will be used. - /// - /// # Caution - /// - Your custom client might not be configured correctly to be able to - /// work - /// in long time durations, see [issue 223]. - /// - /// - If this method is used, the `TELOXIDE_PROXY` environmental variable - /// won't be extracted in [`BotBuilder::build`]. - /// - /// [issue 223]: https://github.com/teloxide/teloxide/issues/223 - /// [`BotBuilder::build`]: crate::BotBuilder::build - #[must_use] - pub fn client(mut self, client: Client) -> Self { - self.client = Some(client); - self - } - - /// Specified a custom token. - /// - /// Otherwise, a token will be extracted from the `TELOXIDE_TOKEN` - /// environmental variable. - #[must_use] - pub fn token(mut self, token: S) -> Self - where - S: Into, - { - self.token = Some(token.into()); - self - } - - /// Specifies [`ParseMode`], which will be used during all calls to: - /// - /// - [`send_message`] - /// - [`send_photo`] - /// - [`send_video`] - /// - [`send_audio`] - /// - [`send_document`] - /// - [`send_animation`] - /// - [`send_voice`] - /// - [`send_poll`] - /// - [`edit_message_text`] - /// - [`edit_message_caption`] - /// - /// [`send_message`]: crate::Bot::send_message - /// [`send_photo`]: crate::Bot::send_photo - /// [`send_video`]: crate::Bot::send_video - /// [`send_audio`]: crate::Bot::send_audio - /// [`send_document`]: crate::Bot::send_document - /// [`send_animation`]: crate::Bot::send_animation - /// [`send_voice`]: crate::Bot::send_voice - /// [`send_poll`]: crate::Bot::send_poll - /// [`edit_message_text`]: crate::Bot::edit_message_text - /// [`edit_message_caption`]: crate::Bot::edit_message_caption - #[must_use] - pub fn parse_mode(mut self, parse_mode: ParseMode) -> Self { - self.parse_mode = Some(parse_mode); - self - } - - /// Builds [`Bot`]. - /// - /// This method will attempt to build a new client with a proxy, specified - /// in the `TELOXIDE_PROXY` (passed into [`reqwest::Proxy::all`]) - /// environmental variable, if a client haven't been specified. If - /// `TELOXIDE_PROXY` is unspecified, it'll use no proxy. - /// - /// # Panics - /// - If cannot get the `TELOXIDE_TOKEN` environmental variable. - /// - If it cannot create [`reqwest::Client`]. - /// - /// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html - /// - /// [`Bot`]: crate::Bot - /// [`reqwest::Proxy::all`]: https://docs.rs/reqwest/latest/reqwest/struct.Proxy.html#method.all - #[must_use] - pub fn build(self) -> Bot { - Bot { - token: self.token.unwrap_or_else(|| get_env(TELOXIDE_TOKEN)).into(), - api_url: ApiUrl::Default, - client: self.client.unwrap_or_else(crate::client_from_env), - parse_mode: self.parse_mode, - } - } -} diff --git a/src/lib.rs b/src/lib.rs index ef19a9c9..54d79c30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,19 +7,19 @@ // ```console // $ RUSTDOCFLAGS="--cfg docsrs" cargo doc --open --all-features // ``` +#![forbid(unsafe_code)] #![cfg_attr(all(docsrs, feature = "nightly"), feature(doc_cfg, doc_spotlight))] #![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] -#![forbid(unsafe_code)] #![cfg_attr(feature = "full", deny(broken_intra_doc_links))] //#![deny(missing_docs)] -#[macro_use] // The internal helper macros. +#[macro_use] mod local_macros; // FIXME(waffle): rethink modules, find a place for wrappers. pub use self::{ - bot::{Bot, BotBuilder}, + bot::Bot, errors::{ApiError, DownloadError, RequestError}, }; diff --git a/src/requests/requester_ext.rs b/src/requests/requester_ext.rs index 1b5530b2..f819b0f3 100644 --- a/src/requests/requester_ext.rs +++ b/src/requests/requester_ext.rs @@ -1,4 +1,4 @@ -use crate::requests::Requester; +use crate::{adaptors::DefaultParseMode, requests::Requester, types::ParseMode}; #[cfg(feature = "cache_me")] use crate::adaptors::CacheMe; @@ -46,6 +46,38 @@ pub trait RequesterExt: Requester { { Throttle::new_spawn(self, limits) } + + /// Specifies default [`ParseMode`], which will be used during all calls to: + /// + /// - [`send_message`] + /// - [`send_photo`] + /// - [`send_video`] + /// - [`send_audio`] + /// - [`send_document`] + /// - [`send_animation`] + /// - [`send_voice`] + /// - [`send_poll`] + /// - [`edit_message_text`] (and [`edit_message_text_inline`]) + /// - [`edit_message_caption`] (and [`edit_message_caption_inline`]) + /// + /// [`send_message`]: crate::Requester::send_message + /// [`send_photo`]: crate::Requester::send_photo + /// [`send_video`]: crate::Requester::send_video + /// [`send_audio`]: crate::Requester::send_audio + /// [`send_document`]: crate::Requester::send_document + /// [`send_animation`]: crate::Requester::send_animation + /// [`send_voice`]: crate::Requester::send_voice + /// [`send_poll`]: crate::Requester::send_poll + /// [`edit_message_text`]: crate::Requester::edit_message_text + /// [`edit_message_text`]: crate::Requester::edit_message_text_inline + /// [`edit_message_caption`]: crate::Requester::edit_message_caption + /// [`edit_message_caption`]: crate::Requester::edit_message_caption_inline + fn parse_mode(self, parse_mode: ParseMode) -> DefaultParseMode + where + Self: Sized, + { + DefaultParseMode::new(self, parse_mode) + } } impl RequesterExt for T