From 1fe4518ee9fb7459688fa0f1ec47107550b0180f Mon Sep 17 00:00:00 2001
From: Waffle <waffle.lapkin@gmail.com>
Date: Mon, 12 Jul 2021 16:58:51 +0300
Subject: [PATCH] Add tools for Bot/Request type erasure

---
 CHANGELOG.md                  |    2 +
 Cargo.toml                    |   12 +-
 examples/erased.rs            |   43 ++
 examples/self_info.rs         |    2 +
 src/adaptors.rs               |   18 +
 src/adaptors/erased.rs        | 1339 +++++++++++++++++++++++++++++++++
 src/lib.rs                    |    2 +
 src/requests/request.rs       |   12 +
 src/requests/requester_ext.rs |   17 +
 9 files changed, 1446 insertions(+), 1 deletion(-)
 create mode 100644 examples/erased.rs
 create mode 100644 src/adaptors/erased.rs

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7502a6de..7f218057 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,11 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added
 
+- `ErasedRequester` bot adaptor, `ErasedRequest` struct, `{Request, RequesterExt}::erase` functions ([#105][pr105])
 - `Trace` bot adaptor ([#104][pr104])
 - `HasPayload`, `Request` and `Requester` implementations for `either::Either` ([#103][pr103])
 
 [pr103]: https://github.com/teloxide/teloxide-core/pull/103
 [pr104]: https://github.com/teloxide/teloxide-core/pull/104
+[pr105]: https://github.com/teloxide/teloxide-core/pull/105
 
 ## 0.3.1 - 2021-07-07
 
diff --git a/Cargo.toml b/Cargo.toml
index 68849a5b..987af2b2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -56,6 +56,9 @@ either = "1.6.1"
 vecrem = { version = "0.1", optional = true }
 bitflags = { version = "1.2", optional = true }
 
+[dev-dependencies]
+pretty_env_logger = "0.4"
+
 [features]
 default = ["native-tls"]
 
@@ -74,13 +77,16 @@ throttle = ["vecrem"]
 # Trace bot adaptor
 trace_adaptor = ["bitflags"]
 
+# Erased bot adaptor
+erased = []
+
 # CacheMe bot adaptor
 cache_me = []
 
 # AutoSend bot adaptor
 auto_send = []
 
-full = ["throttle", "trace_adaptor", "cache_me", "auto_send"]
+full = ["throttle", "trace_adaptor", "erased", "cache_me", "auto_send"]
 
 [package.metadata.docs.rs]
 all-features = true
@@ -89,3 +95,7 @@ rustdoc-args = ["--cfg", "docsrs", "-Znormalize-docs"]
 [[example]]
 name = "self_info"
 required-features = ["tokio/macros", "tokio/rt-multi-thread", "auto_send"]
+
+[[example]]
+name = "erased"
+required-features = ["tokio/macros", "tokio/rt-multi-thread", "auto_send", "erased"]
diff --git a/examples/erased.rs b/examples/erased.rs
new file mode 100644
index 00000000..40195ebd
--- /dev/null
+++ b/examples/erased.rs
@@ -0,0 +1,43 @@
+use std::{env::VarError, time::Duration};
+
+use teloxide_core::{adaptors::trace, prelude::*, types::ChatAction};
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    pretty_env_logger::init();
+
+    let chat_id = std::env::var("CHAT_ID")
+        .expect("Expected CHAT_ID env var")
+        .parse::<i64>()?;
+
+    let trace_settings = match std::env::var("TRACE").as_deref() {
+        Ok("EVERYTHING_VERBOSE") => trace::Settings::TRACE_EVERYTHING_VERBOSE,
+        Ok("EVERYTHING") => trace::Settings::TRACE_EVERYTHING,
+        Ok("REQUESTS_VERBOSE") => trace::Settings::TRACE_REQUESTS_VERBOSE,
+        Ok("REQUESTS") => trace::Settings::TRACE_REQUESTS,
+        Ok("RESPONSES_VERBOSE") => trace::Settings::TRACE_RESPONSES_VERBOSE,
+        Ok("RESPONSES") => trace::Settings::TRACE_RESPONSES,
+        Ok("EMPTY") | Ok("") | Err(VarError::NotPresent) => trace::Settings::empty(),
+        Ok(_) | Err(VarError::NotUnicode(_)) => {
+            panic!(
+                "Expected `TRACE` environment variable to be equal to any of the following: \
+                 `EVERYTHING_VERBOSE`, `EVERYTHING`, `REQUESTS_VERBOSE`, `REQUESTS`, \
+                 `RESPONSES_VERBOSE`, `RESPONSES`, `EMPTY`, `` (empty string)"
+            )
+        }
+    };
+
+    log::info!("Trace settings: {:?}", trace_settings);
+
+    let bot = if trace_settings.is_empty() {
+        Bot::from_env().erase().auto_send()
+    } else {
+        Bot::from_env().trace(trace_settings).erase().auto_send()
+    };
+
+    bot.send_chat_action(chat_id, ChatAction::Typing).await?;
+    tokio::time::sleep(Duration::from_secs(1)).await;
+    bot.send_message(chat_id, "Hey hey hey").await?;
+
+    Ok(())
+}
diff --git a/examples/self_info.rs b/examples/self_info.rs
index 5735bbb3..9c5304ae 100644
--- a/examples/self_info.rs
+++ b/examples/self_info.rs
@@ -5,6 +5,8 @@ use teloxide_core::{
 
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    pretty_env_logger::init();
+
     let chat_id = std::env::var("CHAT_ID")
         .expect("Expected CHAT_ID env var")
         .parse::<i64>()?;
diff --git a/src/adaptors.rs b/src/adaptors.rs
index 36a258b7..6733662b 100644
--- a/src/adaptors.rs
+++ b/src/adaptors.rs
@@ -41,6 +41,18 @@ pub mod cache_me;
 )]
 pub mod trace;
 
+/// [`ErasedErasedRequester`] bot adaptor which allows to erase type of
+/// [`Requester`].
+///
+/// [`ErasedRequester`]: erased::ErasedRequester
+/// [`Requester`]: crate::requests::Requester
+#[cfg(feature = "erased")]
+#[cfg_attr(
+    all(any(docsrs, dep_docsrs), feature = "nightly"),
+    doc(cfg(feature = "erased"))
+)]
+pub mod erased;
+
 /// [`Throttle`] bot adaptor which allows automatically throttle when hitting
 /// API limits.
 ///
@@ -66,6 +78,12 @@ pub use auto_send::AutoSend;
     doc(cfg(feature = "cache_me"))
 )]
 pub use cache_me::CacheMe;
+#[cfg(feature = "erased")]
+#[cfg_attr(
+    all(any(docsrs, dep_docsrs), feature = "nightly"),
+    doc(cfg(feature = "erased"))
+)]
+pub use erased::ErasedRequester;
 #[cfg(feature = "throttle")]
 #[cfg_attr(
     all(any(docsrs, dep_docsrs), feature = "nightly"),
diff --git a/src/adaptors/erased.rs b/src/adaptors/erased.rs
new file mode 100644
index 00000000..7141c9b6
--- /dev/null
+++ b/src/adaptors/erased.rs
@@ -0,0 +1,1339 @@
+use std::sync::Arc;
+
+use futures::{future::BoxFuture, FutureExt};
+use reqwest::Url;
+
+use crate::{
+    payloads::*,
+    requests::{HasPayload, Output, Payload, Request, Requester},
+    types::{
+        BotCommand, ChatAction, ChatId, ChatPermissions, InlineQueryResult, InputFile, InputMedia,
+        InputSticker, LabeledPrice, PassportElementError, PollType, TargetMessage,
+    },
+};
+
+/// [`Requester`] with erased type.
+#[derive(Clone)]
+pub struct ErasedRequester<'a, E> {
+    inner: Arc<dyn ErasableRequester<'a, Err = E> + 'a>,
+}
+
+impl<'a, E> ErasedRequester<'a, E> {
+    /// Erases type of `requester`
+    ///
+    /// Note: it's recommended to use [`RequesterExt::erase`] instead.
+    ///
+    /// [`RequesterExt::erase`]: crate::requests::RequesterExt::erase
+    pub fn new<B>(requester: B) -> Self
+    where
+        B: Requester<Err = E> + 'a,
+    {
+        Self {
+            inner: Arc::new(requester),
+        }
+    }
+}
+
+/// [`Request`] with erased type.
+pub struct ErasedRequest<'a, T, E> {
+    inner: Box<dyn ErasableRequest<'a, Payload = T, Err = E> + 'a>,
+}
+
+impl<'a, T, E> ErasedRequest<'a, T, E> {
+    pub(crate) fn erase(request: impl Request<Payload = T, Err = E> + 'a) -> Self {
+        Self {
+            inner: Box::new(request),
+        }
+    }
+}
+
+impl<T, E> HasPayload for ErasedRequest<'_, T, E>
+where
+    T: Payload,
+{
+    type Payload = T;
+
+    fn payload_mut(&mut self) -> &mut Self::Payload {
+        self.inner.payload_mut()
+    }
+
+    fn payload_ref(&self) -> &Self::Payload {
+        self.inner.payload_ref()
+    }
+}
+
+impl<'a, T, E> Request for ErasedRequest<'a, T, E>
+where
+    T: Payload,
+    E: std::error::Error + Send,
+{
+    type Err = E;
+
+    type Send = BoxFuture<'a, Result<Output<Self>, Self::Err>>;
+
+    type SendRef = BoxFuture<'a, Result<Output<Self>, Self::Err>>;
+
+    fn send(self) -> Self::Send {
+        self.inner.send_box()
+    }
+
+    fn send_ref(&self) -> Self::SendRef {
+        self.inner.send_ref()
+    }
+}
+
+/// Object safe version of [`Request`].
+///
+/// TODO(waffle): make [`Request`] object safe and remove this trait (this is a
+/// breaking change)
+trait ErasableRequest<'a>: HasPayload {
+    type Err: std::error::Error + Send;
+
+    fn send_box(self: Box<Self>) -> BoxFuture<'a, Result<Output<Self>, Self::Err>>;
+
+    fn send_ref(&self) -> BoxFuture<'a, Result<Output<Self>, Self::Err>>;
+}
+
+impl<'a, R> ErasableRequest<'a> for R
+where
+    R: Request,
+    <R as Request>::Send: 'a,
+    <R as Request>::SendRef: 'a,
+{
+    type Err = R::Err;
+
+    fn send_box(self: Box<Self>) -> BoxFuture<'a, Result<Output<Self>, Self::Err>> {
+        self.send().boxed()
+    }
+
+    fn send_ref(&self) -> BoxFuture<'a, Result<Output<Self>, Self::Err>> {
+        Request::send_ref(self).boxed()
+    }
+}
+
+macro_rules! fty {
+    ($T:ident) => {
+        ErasedRequest<'a, $T, Err>
+    };
+}
+
+macro_rules! fwd_erased {
+    ($m:ident $this:ident ($($arg:ident : $T:ty),*)) => {
+        $this.inner.$m($( fwd_erased!(@convert $m, $arg, $arg : $T) ),*)
+    };
+
+    (@convert send_media_group, $arg:ident, media : $T:ty) => {
+        $arg.into_iter().collect()
+    };
+    (@convert $m:ident, $arg:ident, options : $T:ty) => {
+        $arg.into_iter().collect()
+    };
+    (@convert $m:ident, $arg:ident, commands : $T:ty) => {
+        $arg.into_iter().collect()
+    };
+    (@convert $m:ident, $arg:ident, results : $T:ty) => {
+        $arg.into_iter().collect()
+    };
+    (@convert $m:ident, $arg:ident, prices : $T:ty) => {
+        $arg.into_iter().collect()
+    };
+    (@convert $m:ident, $arg:ident, errors : $T:ty) => {
+        $arg.into_iter().collect()
+    };
+    (@convert $m:ident, $arg:ident, $arg_:ident : $T:ty) => {
+        $arg.into()
+    };
+}
+
+impl<'a, Err> Requester for ErasedRequester<'a, Err>
+where
+    Err: std::error::Error + Send,
+{
+    type Err = Err;
+
+    requester_forward! {
+        get_me, log_out, close, get_updates, set_webhook, delete_webhook, get_webhook_info,
+        forward_message, copy_message, send_message, send_photo, send_audio, send_document,
+        send_video, send_animation, send_voice, 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_poll, send_dice, send_chat_action, get_user_profile_photos,
+        get_file, kick_chat_member, ban_chat_member, unban_chat_member, restrict_chat_member,
+        promote_chat_member, set_chat_administrator_custom_title, set_chat_permissions,
+        export_chat_invite_link, create_chat_invite_link, edit_chat_invite_link,
+        revoke_chat_invite_link, set_chat_photo, delete_chat_photo, set_chat_title,
+        set_chat_description, pin_chat_message, unpin_chat_message, unpin_all_chat_messages,
+        leave_chat, get_chat, get_chat_administrators, get_chat_members_count, get_chat_member_count, get_chat_member,
+        set_chat_sticker_set, delete_chat_sticker_set, answer_callback_query,
+        set_my_commands, get_my_commands, delete_my_commands, answer_inline_query, edit_message_text,
+        edit_message_text_inline, edit_message_caption, edit_message_caption_inline,
+        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, send_game,
+        set_game_score, set_game_score_inline, get_game_high_scores,
+        get_updates_fault_tolerant => fwd_erased, fty
+    }
+}
+
+/// Object safe version of [`Requester`].
+trait ErasableRequester<'a> {
+    /// Error type returned by all requests.
+    type Err: std::error::Error + Send;
+
+    fn get_updates(&self) -> ErasedRequest<'a, GetUpdates, Self::Err>;
+
+    fn set_webhook(&self, url: Url) -> ErasedRequest<'a, SetWebhook, Self::Err>;
+
+    fn delete_webhook(&self) -> ErasedRequest<'a, DeleteWebhook, Self::Err>;
+
+    fn get_webhook_info(&self) -> ErasedRequest<'a, GetWebhookInfo, Self::Err>;
+
+    fn get_me(&self) -> ErasedRequest<'a, GetMe, Self::Err>;
+
+    fn log_out(&self) -> ErasedRequest<'a, LogOut, Self::Err>;
+
+    fn close(&self) -> ErasedRequest<'a, Close, Self::Err>;
+
+    fn send_message(
+        &self,
+        chat_id: ChatId,
+        text: String,
+    ) -> ErasedRequest<'a, SendMessage, Self::Err>;
+
+    fn forward_message(
+        &self,
+        chat_id: ChatId,
+        from_chat_id: ChatId,
+        message_id: i32,
+    ) -> ErasedRequest<'a, ForwardMessage, Self::Err>;
+
+    fn copy_message(
+        &self,
+        chat_id: ChatId,
+        from_chat_id: ChatId,
+        message_id: i32,
+    ) -> ErasedRequest<'a, CopyMessage, Self::Err>;
+
+    fn send_photo(
+        &self,
+        chat_id: ChatId,
+        photo: InputFile,
+    ) -> ErasedRequest<'a, SendPhoto, Self::Err>;
+
+    fn send_audio(
+        &self,
+        chat_id: ChatId,
+        audio: InputFile,
+    ) -> ErasedRequest<'a, SendAudio, Self::Err>;
+
+    fn send_document(
+        &self,
+        chat_id: ChatId,
+        document: InputFile,
+    ) -> ErasedRequest<'a, SendDocument, Self::Err>;
+
+    fn send_video(
+        &self,
+        chat_id: ChatId,
+        video: InputFile,
+    ) -> ErasedRequest<'a, SendVideo, Self::Err>;
+
+    fn send_animation(
+        &self,
+        chat_id: ChatId,
+        animation: InputFile,
+    ) -> ErasedRequest<'a, SendAnimation, Self::Err>;
+
+    fn send_voice(
+        &self,
+        chat_id: ChatId,
+        voice: InputFile,
+    ) -> ErasedRequest<'a, SendVoice, Self::Err>;
+
+    fn send_video_note(
+        &self,
+        chat_id: ChatId,
+        video_note: InputFile,
+    ) -> ErasedRequest<'a, SendVideoNote, Self::Err>;
+
+    fn send_media_group(
+        &self,
+        chat_id: ChatId,
+        media: Vec<InputMedia>,
+    ) -> ErasedRequest<'a, SendMediaGroup, Self::Err>;
+
+    fn send_location(
+        &self,
+        chat_id: ChatId,
+        latitude: f64,
+        longitude: f64,
+    ) -> ErasedRequest<'a, SendLocation, Self::Err>;
+
+    fn edit_message_live_location(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+        latitude: f64,
+        longitude: f64,
+    ) -> ErasedRequest<'a, EditMessageLiveLocation, Self::Err>;
+
+    fn edit_message_live_location_inline(
+        &self,
+        inline_message_id: String,
+        latitude: f64,
+        longitude: f64,
+    ) -> ErasedRequest<'a, EditMessageLiveLocationInline, Self::Err>;
+
+    fn stop_message_live_location(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+        latitude: f64,
+        longitude: f64,
+    ) -> ErasedRequest<'a, StopMessageLiveLocation, Self::Err>;
+
+    fn stop_message_live_location_inline(
+        &self,
+        inline_message_id: String,
+        latitude: f64,
+        longitude: f64,
+    ) -> ErasedRequest<'a, StopMessageLiveLocationInline, Self::Err>;
+
+    fn send_venue(
+        &self,
+        chat_id: ChatId,
+        latitude: f64,
+        longitude: f64,
+        title: String,
+        address: String,
+    ) -> ErasedRequest<'a, SendVenue, Self::Err>;
+
+    fn send_contact(
+        &self,
+        chat_id: ChatId,
+        phone_number: String,
+        first_name: String,
+    ) -> ErasedRequest<'a, SendContact, Self::Err>;
+
+    fn send_poll(
+        &self,
+        chat_id: ChatId,
+        question: String,
+        options: Vec<String>,
+        type_: PollType,
+    ) -> ErasedRequest<'a, SendPoll, Self::Err>;
+
+    fn send_dice(&self, chat_id: ChatId) -> ErasedRequest<'a, SendDice, Self::Err>;
+
+    fn send_chat_action(
+        &self,
+        chat_id: ChatId,
+        action: ChatAction,
+    ) -> ErasedRequest<'a, SendChatAction, Self::Err>;
+
+    fn get_user_profile_photos(
+        &self,
+        user_id: i64,
+    ) -> ErasedRequest<'a, GetUserProfilePhotos, Self::Err>;
+
+    fn get_file(&self, file_id: String) -> ErasedRequest<'a, GetFile, Self::Err>;
+
+    fn ban_chat_member(
+        &self,
+        chat_id: ChatId,
+        user_id: i64,
+    ) -> ErasedRequest<'a, BanChatMember, Self::Err>;
+
+    fn kick_chat_member(
+        &self,
+        chat_id: ChatId,
+        user_id: i64,
+    ) -> ErasedRequest<'a, KickChatMember, Self::Err>;
+
+    fn unban_chat_member(
+        &self,
+        chat_id: ChatId,
+        user_id: i64,
+    ) -> ErasedRequest<'a, UnbanChatMember, Self::Err>;
+
+    fn restrict_chat_member(
+        &self,
+        chat_id: ChatId,
+        user_id: i64,
+        permissions: ChatPermissions,
+    ) -> ErasedRequest<'a, RestrictChatMember, Self::Err>;
+
+    fn promote_chat_member(
+        &self,
+        chat_id: ChatId,
+        user_id: i64,
+    ) -> ErasedRequest<'a, PromoteChatMember, Self::Err>;
+
+    fn set_chat_administrator_custom_title(
+        &self,
+        chat_id: ChatId,
+        user_id: i64,
+        custom_title: String,
+    ) -> ErasedRequest<'a, SetChatAdministratorCustomTitle, Self::Err>;
+
+    fn set_chat_permissions(
+        &self,
+        chat_id: ChatId,
+        permissions: ChatPermissions,
+    ) -> ErasedRequest<'a, SetChatPermissions, Self::Err>;
+
+    fn export_chat_invite_link(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, ExportChatInviteLink, Self::Err>;
+
+    fn create_chat_invite_link(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, CreateChatInviteLink, Self::Err>;
+
+    fn edit_chat_invite_link(
+        &self,
+        chat_id: ChatId,
+        invite_link: String,
+    ) -> ErasedRequest<'a, EditChatInviteLink, Self::Err>;
+
+    fn revoke_chat_invite_link(
+        &self,
+        chat_id: ChatId,
+        invite_link: String,
+    ) -> ErasedRequest<'a, RevokeChatInviteLink, Self::Err>;
+
+    fn set_chat_photo(
+        &self,
+        chat_id: ChatId,
+        photo: InputFile,
+    ) -> ErasedRequest<'a, SetChatPhoto, Self::Err>;
+
+    fn delete_chat_photo(&self, chat_id: ChatId) -> ErasedRequest<'a, DeleteChatPhoto, Self::Err>;
+
+    fn set_chat_title(
+        &self,
+        chat_id: ChatId,
+        title: String,
+    ) -> ErasedRequest<'a, SetChatTitle, Self::Err>;
+
+    fn set_chat_description(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, SetChatDescription, Self::Err>;
+
+    fn pin_chat_message(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+    ) -> ErasedRequest<'a, PinChatMessage, Self::Err>;
+
+    fn unpin_chat_message(&self, chat_id: ChatId)
+        -> ErasedRequest<'a, UnpinChatMessage, Self::Err>;
+
+    fn unpin_all_chat_messages(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, UnpinAllChatMessages, Self::Err>;
+
+    fn leave_chat(&self, chat_id: ChatId) -> ErasedRequest<'a, LeaveChat, Self::Err>;
+
+    fn get_chat(&self, chat_id: ChatId) -> ErasedRequest<'a, GetChat, Self::Err>;
+
+    fn get_chat_administrators(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, GetChatAdministrators, Self::Err>;
+
+    fn get_chat_member_count(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, GetChatMemberCount, Self::Err>;
+
+    fn get_chat_members_count(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, GetChatMembersCount, Self::Err>;
+
+    fn get_chat_member(
+        &self,
+        chat_id: ChatId,
+        user_id: i64,
+    ) -> ErasedRequest<'a, GetChatMember, Self::Err>;
+
+    fn set_chat_sticker_set(
+        &self,
+        chat_id: ChatId,
+        sticker_set_name: String,
+    ) -> ErasedRequest<'a, SetChatStickerSet, Self::Err>;
+
+    fn delete_chat_sticker_set(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, DeleteChatStickerSet, Self::Err>;
+
+    fn answer_callback_query(
+        &self,
+        callback_query_id: String,
+    ) -> ErasedRequest<'a, AnswerCallbackQuery, Self::Err>;
+
+    fn set_my_commands(
+        &self,
+        commands: Vec<BotCommand>,
+    ) -> ErasedRequest<'a, SetMyCommands, Self::Err>;
+
+    fn get_my_commands(&self) -> ErasedRequest<'a, GetMyCommands, Self::Err>;
+
+    fn delete_my_commands(&self) -> ErasedRequest<'a, DeleteMyCommands, Self::Err>;
+
+    fn answer_inline_query(
+        &self,
+        inline_query_id: String,
+        results: Vec<InlineQueryResult>,
+    ) -> ErasedRequest<'a, AnswerInlineQuery, Self::Err>;
+
+    fn edit_message_text(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+        text: String,
+    ) -> ErasedRequest<'a, EditMessageText, Self::Err>;
+
+    fn edit_message_text_inline(
+        &self,
+        inline_message_id: String,
+        text: String,
+    ) -> ErasedRequest<'a, EditMessageTextInline, Self::Err>;
+
+    fn edit_message_caption(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+    ) -> ErasedRequest<'a, EditMessageCaption, Self::Err>;
+
+    fn edit_message_caption_inline(
+        &self,
+        inline_message_id: String,
+    ) -> ErasedRequest<'a, EditMessageCaptionInline, Self::Err>;
+
+    fn edit_message_media(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+        media: InputMedia,
+    ) -> ErasedRequest<'a, EditMessageMedia, Self::Err>;
+
+    fn edit_message_media_inline(
+        &self,
+        inline_message_id: String,
+        media: InputMedia,
+    ) -> ErasedRequest<'a, EditMessageMediaInline, Self::Err>;
+
+    fn edit_message_reply_markup(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+    ) -> ErasedRequest<'a, EditMessageReplyMarkup, Self::Err>;
+
+    fn edit_message_reply_markup_inline(
+        &self,
+        inline_message_id: String,
+    ) -> ErasedRequest<'a, EditMessageReplyMarkupInline, Self::Err>;
+
+    fn stop_poll(&self, chat_id: ChatId, message_id: i32)
+        -> ErasedRequest<'a, StopPoll, Self::Err>;
+
+    fn delete_message(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+    ) -> ErasedRequest<'a, DeleteMessage, Self::Err>;
+
+    fn send_sticker(
+        &self,
+        chat_id: ChatId,
+        sticker: InputFile,
+    ) -> ErasedRequest<'a, SendSticker, Self::Err>;
+
+    fn get_sticker_set(&self, name: String) -> ErasedRequest<'a, GetStickerSet, Self::Err>;
+
+    fn upload_sticker_file(
+        &self,
+        user_id: i64,
+        png_sticker: InputFile,
+    ) -> ErasedRequest<'a, UploadStickerFile, Self::Err>;
+
+    fn create_new_sticker_set(
+        &self,
+        user_id: i64,
+        name: String,
+        title: String,
+        sticker: InputSticker,
+        emojis: String,
+    ) -> ErasedRequest<'a, CreateNewStickerSet, Self::Err>;
+
+    fn add_sticker_to_set(
+        &self,
+        user_id: i64,
+        name: String,
+        sticker: InputSticker,
+        emojis: String,
+    ) -> ErasedRequest<'a, AddStickerToSet, Self::Err>;
+
+    fn set_sticker_position_in_set(
+        &self,
+        sticker: String,
+        position: u32,
+    ) -> ErasedRequest<'a, SetStickerPositionInSet, Self::Err>;
+
+    fn delete_sticker_from_set(
+        &self,
+        sticker: String,
+    ) -> ErasedRequest<'a, DeleteStickerFromSet, Self::Err>;
+
+    fn set_sticker_set_thumb(
+        &self,
+        name: String,
+        user_id: i64,
+    ) -> ErasedRequest<'a, SetStickerSetThumb, Self::Err>;
+
+    fn send_invoice(
+        &self,
+        chat_id: ChatId,
+        title: String,
+        description: String,
+        payload: String,
+        provider_token: String,
+        currency: String,
+        prices: Vec<LabeledPrice>,
+    ) -> ErasedRequest<'a, SendInvoice, Self::Err>;
+
+    fn answer_shipping_query(
+        &self,
+        shipping_query_id: String,
+        ok: bool,
+    ) -> ErasedRequest<'a, AnswerShippingQuery, Self::Err>;
+
+    fn answer_pre_checkout_query(
+        &self,
+        pre_checkout_query_id: String,
+        ok: bool,
+    ) -> ErasedRequest<'a, AnswerPreCheckoutQuery, Self::Err>;
+
+    fn set_passport_data_errors(
+        &self,
+        user_id: i64,
+        errors: Vec<PassportElementError>,
+    ) -> ErasedRequest<'a, SetPassportDataErrors, Self::Err>;
+
+    fn send_game(
+        &self,
+        chat_id: u32,
+        game_short_name: String,
+    ) -> ErasedRequest<'a, SendGame, Self::Err>;
+
+    fn set_game_score(
+        &self,
+        user_id: i64,
+        score: u64,
+        chat_id: u32,
+        message_id: i64,
+    ) -> ErasedRequest<'a, SetGameScore, Self::Err>;
+
+    fn set_game_score_inline(
+        &self,
+        user_id: i64,
+        score: u64,
+        inline_message_id: String,
+    ) -> ErasedRequest<'a, SetGameScoreInline, Self::Err>;
+
+    fn get_game_high_scores(
+        &self,
+        user_id: i64,
+        target: TargetMessage,
+    ) -> ErasedRequest<'a, GetGameHighScores, Self::Err>;
+
+    fn get_updates_fault_tolerant(&self) -> ErasedRequest<'a, GetUpdatesFaultTolerant, Self::Err>;
+}
+
+impl<'a, B> ErasableRequester<'a> for B
+where
+    B: Requester + 'a,
+{
+    type Err = B::Err;
+
+    fn get_updates(&self) -> ErasedRequest<'a, GetUpdates, Self::Err> {
+        Requester::get_updates(self).erase()
+    }
+
+    fn set_webhook(&self, url: Url) -> ErasedRequest<'a, SetWebhook, Self::Err> {
+        Requester::set_webhook(self, url).erase()
+    }
+
+    fn delete_webhook(&self) -> ErasedRequest<'a, DeleteWebhook, Self::Err> {
+        Requester::delete_webhook(self).erase()
+    }
+
+    fn get_webhook_info(&self) -> ErasedRequest<'a, GetWebhookInfo, Self::Err> {
+        Requester::get_webhook_info(self).erase()
+    }
+
+    fn get_me(&self) -> ErasedRequest<'a, GetMe, Self::Err> {
+        Requester::get_me(self).erase()
+    }
+
+    fn log_out(&self) -> ErasedRequest<'a, LogOut, Self::Err> {
+        Requester::log_out(self).erase()
+    }
+
+    fn close(&self) -> ErasedRequest<'a, Close, Self::Err> {
+        Requester::close(self).erase()
+    }
+
+    fn send_message(
+        &self,
+        chat_id: ChatId,
+        text: String,
+    ) -> ErasedRequest<'a, SendMessage, Self::Err> {
+        Requester::send_message(self, chat_id, text).erase()
+    }
+
+    fn forward_message(
+        &self,
+        chat_id: ChatId,
+        from_chat_id: ChatId,
+        message_id: i32,
+    ) -> ErasedRequest<'a, ForwardMessage, Self::Err> {
+        Requester::forward_message(self, chat_id, from_chat_id, message_id).erase()
+    }
+
+    fn copy_message(
+        &self,
+        chat_id: ChatId,
+        from_chat_id: ChatId,
+        message_id: i32,
+    ) -> ErasedRequest<'a, CopyMessage, Self::Err> {
+        Requester::copy_message(self, chat_id, from_chat_id, message_id).erase()
+    }
+
+    fn send_photo(
+        &self,
+        chat_id: ChatId,
+        photo: InputFile,
+    ) -> ErasedRequest<'a, SendPhoto, Self::Err> {
+        Requester::send_photo(self, chat_id, photo).erase()
+    }
+
+    fn send_audio(
+        &self,
+        chat_id: ChatId,
+        audio: InputFile,
+    ) -> ErasedRequest<'a, SendAudio, Self::Err> {
+        Requester::send_audio(self, chat_id, audio).erase()
+    }
+
+    fn send_document(
+        &self,
+        chat_id: ChatId,
+        document: InputFile,
+    ) -> ErasedRequest<'a, SendDocument, Self::Err> {
+        Requester::send_document(self, chat_id, document).erase()
+    }
+
+    fn send_video(
+        &self,
+        chat_id: ChatId,
+        video: InputFile,
+    ) -> ErasedRequest<'a, SendVideo, Self::Err> {
+        Requester::send_video(self, chat_id, video).erase()
+    }
+
+    fn send_animation(
+        &self,
+        chat_id: ChatId,
+        animation: InputFile,
+    ) -> ErasedRequest<'a, SendAnimation, Self::Err> {
+        Requester::send_animation(self, chat_id, animation).erase()
+    }
+
+    fn send_voice(
+        &self,
+        chat_id: ChatId,
+        voice: InputFile,
+    ) -> ErasedRequest<'a, SendVoice, Self::Err> {
+        Requester::send_voice(self, chat_id, voice).erase()
+    }
+
+    fn send_video_note(
+        &self,
+        chat_id: ChatId,
+        video_note: InputFile,
+    ) -> ErasedRequest<'a, SendVideoNote, Self::Err> {
+        Requester::send_video_note(self, chat_id, video_note).erase()
+    }
+
+    fn send_media_group(
+        &self,
+        chat_id: ChatId,
+        media: Vec<InputMedia>,
+    ) -> ErasedRequest<'a, SendMediaGroup, Self::Err> {
+        Requester::send_media_group(self, chat_id, media).erase()
+    }
+
+    fn send_location(
+        &self,
+        chat_id: ChatId,
+        latitude: f64,
+        longitude: f64,
+    ) -> ErasedRequest<'a, SendLocation, Self::Err> {
+        Requester::send_location(self, chat_id, latitude, longitude).erase()
+    }
+
+    fn edit_message_live_location(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+        latitude: f64,
+        longitude: f64,
+    ) -> ErasedRequest<'a, EditMessageLiveLocation, Self::Err> {
+        Requester::edit_message_live_location(self, chat_id, message_id, latitude, longitude)
+            .erase()
+    }
+
+    fn edit_message_live_location_inline(
+        &self,
+        inline_message_id: String,
+        latitude: f64,
+        longitude: f64,
+    ) -> ErasedRequest<'a, EditMessageLiveLocationInline, Self::Err> {
+        Requester::edit_message_live_location_inline(self, inline_message_id, latitude, longitude)
+            .erase()
+    }
+
+    fn stop_message_live_location(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+        latitude: f64,
+        longitude: f64,
+    ) -> ErasedRequest<'a, StopMessageLiveLocation, Self::Err> {
+        Requester::stop_message_live_location(self, chat_id, message_id, latitude, longitude)
+            .erase()
+    }
+
+    fn stop_message_live_location_inline(
+        &self,
+        inline_message_id: String,
+        latitude: f64,
+        longitude: f64,
+    ) -> ErasedRequest<'a, StopMessageLiveLocationInline, Self::Err> {
+        Requester::stop_message_live_location_inline(self, inline_message_id, latitude, longitude)
+            .erase()
+    }
+
+    fn send_venue(
+        &self,
+        chat_id: ChatId,
+        latitude: f64,
+        longitude: f64,
+        title: String,
+        address: String,
+    ) -> ErasedRequest<'a, SendVenue, Self::Err> {
+        Requester::send_venue(self, chat_id, latitude, longitude, title, address).erase()
+    }
+
+    fn send_contact(
+        &self,
+        chat_id: ChatId,
+        phone_number: String,
+        first_name: String,
+    ) -> ErasedRequest<'a, SendContact, Self::Err> {
+        Requester::send_contact(self, chat_id, phone_number, first_name).erase()
+    }
+
+    fn send_poll(
+        &self,
+        chat_id: ChatId,
+        question: String,
+        options: Vec<String>,
+        type_: PollType,
+    ) -> ErasedRequest<'a, SendPoll, Self::Err> {
+        Requester::send_poll(self, chat_id, question, options, type_).erase()
+    }
+
+    fn send_dice(&self, chat_id: ChatId) -> ErasedRequest<'a, SendDice, Self::Err> {
+        Requester::send_dice(self, chat_id).erase()
+    }
+
+    fn send_chat_action(
+        &self,
+        chat_id: ChatId,
+        action: ChatAction,
+    ) -> ErasedRequest<'a, SendChatAction, Self::Err> {
+        Requester::send_chat_action(self, chat_id, action).erase()
+    }
+
+    fn get_user_profile_photos(
+        &self,
+        user_id: i64,
+    ) -> ErasedRequest<'a, GetUserProfilePhotos, Self::Err> {
+        Requester::get_user_profile_photos(self, user_id).erase()
+    }
+
+    fn get_file(&self, file_id: String) -> ErasedRequest<'a, GetFile, Self::Err> {
+        Requester::get_file(self, file_id).erase()
+    }
+
+    fn ban_chat_member(
+        &self,
+        chat_id: ChatId,
+        user_id: i64,
+    ) -> ErasedRequest<'a, BanChatMember, Self::Err> {
+        Requester::ban_chat_member(self, chat_id, user_id).erase()
+    }
+
+    fn kick_chat_member(
+        &self,
+        chat_id: ChatId,
+        user_id: i64,
+    ) -> ErasedRequest<'a, KickChatMember, Self::Err> {
+        Requester::kick_chat_member(self, chat_id, user_id).erase()
+    }
+
+    fn unban_chat_member(
+        &self,
+        chat_id: ChatId,
+        user_id: i64,
+    ) -> ErasedRequest<'a, UnbanChatMember, Self::Err> {
+        Requester::unban_chat_member(self, chat_id, user_id).erase()
+    }
+
+    fn restrict_chat_member(
+        &self,
+        chat_id: ChatId,
+        user_id: i64,
+        permissions: ChatPermissions,
+    ) -> ErasedRequest<'a, RestrictChatMember, Self::Err> {
+        Requester::restrict_chat_member(self, chat_id, user_id, permissions).erase()
+    }
+
+    fn promote_chat_member(
+        &self,
+        chat_id: ChatId,
+        user_id: i64,
+    ) -> ErasedRequest<'a, PromoteChatMember, Self::Err> {
+        Requester::promote_chat_member(self, chat_id, user_id).erase()
+    }
+
+    fn set_chat_administrator_custom_title(
+        &self,
+        chat_id: ChatId,
+        user_id: i64,
+        custom_title: String,
+    ) -> ErasedRequest<'a, SetChatAdministratorCustomTitle, Self::Err> {
+        Requester::set_chat_administrator_custom_title(self, chat_id, user_id, custom_title).erase()
+    }
+
+    fn set_chat_permissions(
+        &self,
+        chat_id: ChatId,
+        permissions: ChatPermissions,
+    ) -> ErasedRequest<'a, SetChatPermissions, Self::Err> {
+        Requester::set_chat_permissions(self, chat_id, permissions).erase()
+    }
+
+    fn export_chat_invite_link(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, ExportChatInviteLink, Self::Err> {
+        Requester::export_chat_invite_link(self, chat_id).erase()
+    }
+
+    fn create_chat_invite_link(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, CreateChatInviteLink, Self::Err> {
+        Requester::create_chat_invite_link(self, chat_id).erase()
+    }
+
+    fn edit_chat_invite_link(
+        &self,
+        chat_id: ChatId,
+        invite_link: String,
+    ) -> ErasedRequest<'a, EditChatInviteLink, Self::Err> {
+        Requester::edit_chat_invite_link(self, chat_id, invite_link).erase()
+    }
+
+    fn revoke_chat_invite_link(
+        &self,
+        chat_id: ChatId,
+        invite_link: String,
+    ) -> ErasedRequest<'a, RevokeChatInviteLink, Self::Err> {
+        Requester::revoke_chat_invite_link(self, chat_id, invite_link).erase()
+    }
+
+    fn set_chat_photo(
+        &self,
+        chat_id: ChatId,
+        photo: InputFile,
+    ) -> ErasedRequest<'a, SetChatPhoto, Self::Err> {
+        Requester::set_chat_photo(self, chat_id, photo).erase()
+    }
+
+    fn delete_chat_photo(&self, chat_id: ChatId) -> ErasedRequest<'a, DeleteChatPhoto, Self::Err> {
+        Requester::delete_chat_photo(self, chat_id).erase()
+    }
+
+    fn set_chat_title(
+        &self,
+        chat_id: ChatId,
+        title: String,
+    ) -> ErasedRequest<'a, SetChatTitle, Self::Err> {
+        Requester::set_chat_title(self, chat_id, title).erase()
+    }
+
+    fn set_chat_description(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, SetChatDescription, Self::Err> {
+        Requester::set_chat_description(self, chat_id).erase()
+    }
+
+    fn pin_chat_message(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+    ) -> ErasedRequest<'a, PinChatMessage, Self::Err> {
+        Requester::pin_chat_message(self, chat_id, message_id).erase()
+    }
+
+    fn unpin_chat_message(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, UnpinChatMessage, Self::Err> {
+        Requester::unpin_chat_message(self, chat_id).erase()
+    }
+
+    fn unpin_all_chat_messages(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, UnpinAllChatMessages, Self::Err> {
+        Requester::unpin_all_chat_messages(self, chat_id).erase()
+    }
+
+    fn leave_chat(&self, chat_id: ChatId) -> ErasedRequest<'a, LeaveChat, Self::Err> {
+        Requester::leave_chat(self, chat_id).erase()
+    }
+
+    fn get_chat(&self, chat_id: ChatId) -> ErasedRequest<'a, GetChat, Self::Err> {
+        Requester::get_chat(self, chat_id).erase()
+    }
+
+    fn get_chat_administrators(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, GetChatAdministrators, Self::Err> {
+        Requester::get_chat_administrators(self, chat_id).erase()
+    }
+
+    fn get_chat_member_count(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, GetChatMemberCount, Self::Err> {
+        Requester::get_chat_member_count(self, chat_id).erase()
+    }
+
+    fn get_chat_members_count(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, GetChatMembersCount, Self::Err> {
+        Requester::get_chat_members_count(self, chat_id).erase()
+    }
+
+    fn get_chat_member(
+        &self,
+        chat_id: ChatId,
+        user_id: i64,
+    ) -> ErasedRequest<'a, GetChatMember, Self::Err> {
+        Requester::get_chat_member(self, chat_id, user_id).erase()
+    }
+
+    fn set_chat_sticker_set(
+        &self,
+        chat_id: ChatId,
+        sticker_set_name: String,
+    ) -> ErasedRequest<'a, SetChatStickerSet, Self::Err> {
+        Requester::set_chat_sticker_set(self, chat_id, sticker_set_name).erase()
+    }
+
+    fn delete_chat_sticker_set(
+        &self,
+        chat_id: ChatId,
+    ) -> ErasedRequest<'a, DeleteChatStickerSet, Self::Err> {
+        Requester::delete_chat_sticker_set(self, chat_id).erase()
+    }
+
+    fn answer_callback_query(
+        &self,
+        callback_query_id: String,
+    ) -> ErasedRequest<'a, AnswerCallbackQuery, Self::Err> {
+        Requester::answer_callback_query(self, callback_query_id).erase()
+    }
+
+    fn set_my_commands(
+        &self,
+        commands: Vec<BotCommand>,
+    ) -> ErasedRequest<'a, SetMyCommands, Self::Err> {
+        Requester::set_my_commands(self, commands).erase()
+    }
+
+    fn get_my_commands(&self) -> ErasedRequest<'a, GetMyCommands, Self::Err> {
+        Requester::get_my_commands(self).erase()
+    }
+
+    fn delete_my_commands(&self) -> ErasedRequest<'a, DeleteMyCommands, Self::Err> {
+        Requester::delete_my_commands(self).erase()
+    }
+
+    fn answer_inline_query(
+        &self,
+        inline_query_id: String,
+        results: Vec<InlineQueryResult>,
+    ) -> ErasedRequest<'a, AnswerInlineQuery, Self::Err> {
+        Requester::answer_inline_query(self, inline_query_id, results).erase()
+    }
+
+    fn edit_message_text(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+        text: String,
+    ) -> ErasedRequest<'a, EditMessageText, Self::Err> {
+        Requester::edit_message_text(self, chat_id, message_id, text).erase()
+    }
+
+    fn edit_message_text_inline(
+        &self,
+        inline_message_id: String,
+        text: String,
+    ) -> ErasedRequest<'a, EditMessageTextInline, Self::Err> {
+        Requester::edit_message_text_inline(self, inline_message_id, text).erase()
+    }
+
+    fn edit_message_caption(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+    ) -> ErasedRequest<'a, EditMessageCaption, Self::Err> {
+        Requester::edit_message_caption(self, chat_id, message_id).erase()
+    }
+
+    fn edit_message_caption_inline(
+        &self,
+        inline_message_id: String,
+    ) -> ErasedRequest<'a, EditMessageCaptionInline, Self::Err> {
+        Requester::edit_message_caption_inline(self, inline_message_id).erase()
+    }
+
+    fn edit_message_media(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+        media: InputMedia,
+    ) -> ErasedRequest<'a, EditMessageMedia, Self::Err> {
+        Requester::edit_message_media(self, chat_id, message_id, media).erase()
+    }
+
+    fn edit_message_media_inline(
+        &self,
+        inline_message_id: String,
+        media: InputMedia,
+    ) -> ErasedRequest<'a, EditMessageMediaInline, Self::Err> {
+        Requester::edit_message_media_inline(self, inline_message_id, media).erase()
+    }
+
+    fn edit_message_reply_markup(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+    ) -> ErasedRequest<'a, EditMessageReplyMarkup, Self::Err> {
+        Requester::edit_message_reply_markup(self, chat_id, message_id).erase()
+    }
+
+    fn edit_message_reply_markup_inline(
+        &self,
+        inline_message_id: String,
+    ) -> ErasedRequest<'a, EditMessageReplyMarkupInline, Self::Err> {
+        Requester::edit_message_reply_markup_inline(self, inline_message_id).erase()
+    }
+
+    fn stop_poll(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+    ) -> ErasedRequest<'a, StopPoll, Self::Err> {
+        Requester::stop_poll(self, chat_id, message_id).erase()
+    }
+
+    fn delete_message(
+        &self,
+        chat_id: ChatId,
+        message_id: i32,
+    ) -> ErasedRequest<'a, DeleteMessage, Self::Err> {
+        Requester::delete_message(self, chat_id, message_id).erase()
+    }
+
+    fn send_sticker(
+        &self,
+        chat_id: ChatId,
+        sticker: InputFile,
+    ) -> ErasedRequest<'a, SendSticker, Self::Err> {
+        Requester::send_sticker(self, chat_id, sticker).erase()
+    }
+
+    fn get_sticker_set(&self, name: String) -> ErasedRequest<'a, GetStickerSet, Self::Err> {
+        Requester::get_sticker_set(self, name).erase()
+    }
+
+    fn upload_sticker_file(
+        &self,
+        user_id: i64,
+        png_sticker: InputFile,
+    ) -> ErasedRequest<'a, UploadStickerFile, Self::Err> {
+        Requester::upload_sticker_file(self, user_id, png_sticker).erase()
+    }
+
+    fn create_new_sticker_set(
+        &self,
+        user_id: i64,
+        name: String,
+        title: String,
+        sticker: InputSticker,
+        emojis: String,
+    ) -> ErasedRequest<'a, CreateNewStickerSet, Self::Err> {
+        Requester::create_new_sticker_set(self, user_id, name, title, sticker, emojis).erase()
+    }
+
+    fn add_sticker_to_set(
+        &self,
+        user_id: i64,
+        name: String,
+        sticker: InputSticker,
+        emojis: String,
+    ) -> ErasedRequest<'a, AddStickerToSet, Self::Err> {
+        Requester::add_sticker_to_set(self, user_id, name, sticker, emojis).erase()
+    }
+
+    fn set_sticker_position_in_set(
+        &self,
+        sticker: String,
+        position: u32,
+    ) -> ErasedRequest<'a, SetStickerPositionInSet, Self::Err> {
+        Requester::set_sticker_position_in_set(self, sticker, position).erase()
+    }
+
+    fn delete_sticker_from_set(
+        &self,
+        sticker: String,
+    ) -> ErasedRequest<'a, DeleteStickerFromSet, Self::Err> {
+        Requester::delete_sticker_from_set(self, sticker).erase()
+    }
+
+    fn set_sticker_set_thumb(
+        &self,
+        name: String,
+        user_id: i64,
+    ) -> ErasedRequest<'a, SetStickerSetThumb, Self::Err> {
+        Requester::set_sticker_set_thumb(self, name, user_id).erase()
+    }
+
+    fn send_invoice(
+        &self,
+        chat_id: ChatId,
+        title: String,
+        description: String,
+        payload: String,
+        provider_token: String,
+        currency: String,
+        prices: Vec<LabeledPrice>,
+    ) -> ErasedRequest<'a, SendInvoice, Self::Err> {
+        Requester::send_invoice(
+            self,
+            chat_id,
+            title,
+            description,
+            payload,
+            provider_token,
+            currency,
+            prices,
+        )
+        .erase()
+    }
+
+    fn answer_shipping_query(
+        &self,
+        shipping_query_id: String,
+        ok: bool,
+    ) -> ErasedRequest<'a, AnswerShippingQuery, Self::Err> {
+        Requester::answer_shipping_query(self, shipping_query_id, ok).erase()
+    }
+
+    fn answer_pre_checkout_query(
+        &self,
+        pre_checkout_query_id: String,
+        ok: bool,
+    ) -> ErasedRequest<'a, AnswerPreCheckoutQuery, Self::Err> {
+        Requester::answer_pre_checkout_query(self, pre_checkout_query_id, ok).erase()
+    }
+
+    fn set_passport_data_errors(
+        &self,
+        user_id: i64,
+        errors: Vec<PassportElementError>,
+    ) -> ErasedRequest<'a, SetPassportDataErrors, Self::Err> {
+        Requester::set_passport_data_errors(self, user_id, errors).erase()
+    }
+
+    fn send_game(
+        &self,
+        chat_id: u32,
+        game_short_name: String,
+    ) -> ErasedRequest<'a, SendGame, Self::Err> {
+        Requester::send_game(self, chat_id, game_short_name).erase()
+    }
+
+    fn set_game_score(
+        &self,
+        user_id: i64,
+        score: u64,
+        chat_id: u32,
+        message_id: i64,
+    ) -> ErasedRequest<'a, SetGameScore, Self::Err> {
+        Requester::set_game_score(self, user_id, score, chat_id, message_id).erase()
+    }
+
+    fn set_game_score_inline(
+        &self,
+        user_id: i64,
+        score: u64,
+        inline_message_id: String,
+    ) -> ErasedRequest<'a, SetGameScoreInline, Self::Err> {
+        Requester::set_game_score_inline(self, user_id, score, inline_message_id).erase()
+    }
+
+    fn get_game_high_scores(
+        &self,
+        user_id: i64,
+        target: TargetMessage,
+    ) -> ErasedRequest<'a, GetGameHighScores, Self::Err> {
+        Requester::get_game_high_scores(self, user_id, target).erase()
+    }
+
+    fn get_updates_fault_tolerant(&self) -> ErasedRequest<'a, GetUpdatesFaultTolerant, Self::Err> {
+        Requester::get_updates_fault_tolerant(self).erase()
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 788abe5b..a1ad15d4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -48,6 +48,7 @@
 //! - `rustls` — use [`rustls`] tls implementation
 //! - `auto_send` — enables [`AutoSend`] bot adaptor
 //! - `trace_adaptor` — enables [`Trace`] bot adaptor
+//! - `erased` — enables [`ErasedRequester`] bot adaptor
 //! - `throttle` — enables [`Throttle`] bot adaptor
 //! - `cache_me` — enables [`CacheMe`] bot adaptor
 //! - `full` — enables all features except `nigthly`
@@ -57,6 +58,7 @@
 //!
 //! [`AutoSend`]: adaptors::AutoSend
 //! [`Trace`]: adaptors::Trace
+//! [`ErasedRequester`]: adaptors::ErasedRequester
 //! [`Throttle`]: adaptors::Throttle
 //! [`CacheMe`]: adaptors::CacheMe
 //! [`native-tls`]: https://docs.rs/native-tls
diff --git a/src/requests/request.rs b/src/requests/request.rs
index aed2490e..3913dcb3 100644
--- a/src/requests/request.rs
+++ b/src/requests/request.rs
@@ -87,6 +87,18 @@ pub trait Request: HasPayload {
     /// # };
     /// ```
     fn send_ref(&self) -> Self::SendRef;
+
+    #[cfg(feature = "erased")]
+    #[cfg_attr(
+        all(any(docsrs, dep_docsrs), feature = "nightly"),
+        doc(cfg(feature = "erased"))
+    )]
+    fn erase<'a>(self) -> crate::adaptors::erased::ErasedRequest<'a, Self::Payload, Self::Err>
+    where
+        Self: Sized + 'a,
+    {
+        crate::adaptors::erased::ErasedRequest::erase(self)
+    }
 }
 
 impl<L, R> Request for Either<L, R>
diff --git a/src/requests/requester_ext.rs b/src/requests/requester_ext.rs
index 424796b8..24926ee7 100644
--- a/src/requests/requester_ext.rs
+++ b/src/requests/requester_ext.rs
@@ -6,6 +6,9 @@ use crate::adaptors::CacheMe;
 #[cfg(feature = "auto_send")]
 use crate::adaptors::AutoSend;
 
+#[cfg(feature = "erased")]
+use crate::adaptors::ErasedRequester;
+
 #[cfg(feature = "trace_adaptor")]
 use crate::adaptors::trace::{Settings, Trace};
 
@@ -42,6 +45,20 @@ pub trait RequesterExt: Requester {
         AutoSend::new(self)
     }
 
+    /// Erase requester type.
+    #[cfg(feature = "erased")]
+    #[cfg_attr(
+        all(any(docsrs, dep_docsrs), feature = "nightly"),
+        doc(cfg(feature = "erased"))
+    )]
+    fn erase<'a>(self) -> ErasedRequester<'a, Self::Err>
+    where
+        Self: 'a,
+        Self: Sized,
+    {
+        ErasedRequester::new(self)
+    }
+
     /// Trace requests, see [`Trace`] for more.
     #[cfg(feature = "trace_adaptor")]
     #[cfg_attr(