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(