teloxide_core/adaptors/
trace.rs

1use std::{
2    fmt::Debug,
3    future::{Future, IntoFuture},
4    pin::Pin,
5    task::{self, Poll},
6};
7
8use futures::ready;
9use url::Url;
10
11use crate::{
12    requests::{HasPayload, Output, Payload, Request, Requester},
13    types::*,
14};
15
16/// Trace requests and responses.
17///
18/// This is a tool for debugging.
19///
20/// Depending on [`Settings`] and `log` facade this adaptor may output messages
21/// like these:
22/// ```text
23/// TRACE teloxide_core::adaptors::trace > Sending `SendDice` request
24/// TRACE teloxide_core::adaptors::trace > Got response from `SendDice` request
25/// TRACE teloxide_core::adaptors::trace > Sending `SendDice` request: SendDice { chat_id: Id(0), emoji: Some(Dice), disable_notification: None, reply_to_message_id: None, allow_sending_without_reply: None, reply_markup: None }
26/// TRACE teloxide_core::adaptors::trace > Got response from `SendDice` request: Ok(Message { id: 13812, date: 1625926524, chat: Chat { .. }, via_bot: None, kind: Dice(MessageDice { dice: Dice { emoji: Dice, value: 3 } }) })
27/// ```
28#[derive(Clone, Debug)]
29pub struct Trace<B> {
30    inner: B,
31    settings: Settings,
32}
33
34impl<B> Trace<B> {
35    pub fn new(inner: B, settings: Settings) -> Self {
36        Self { inner, settings }
37    }
38
39    pub fn inner(&self) -> &B {
40        &self.inner
41    }
42
43    pub fn into_inner(self) -> B {
44        self.inner
45    }
46
47    pub fn settings(&self) -> Settings {
48        self.settings.clone()
49    }
50}
51
52bitflags::bitflags! {
53    /// [`Trace`] settings that determine what will be logged.
54    ///
55    /// ## Examples
56    ///
57    /// ```
58    /// use teloxide_core::adaptors::trace::Settings;
59    ///
60    /// // Trace nothing
61    /// let _ = Settings::empty();
62    /// // Trace only requests
63    /// let _ = Settings::TRACE_REQUESTS;
64    /// // Trace requests verbosely and responses (non verbosely)
65    /// let _ = Settings::TRACE_REQUESTS_VERBOSE | Settings::TRACE_RESPONSES;
66    /// ```
67    #[derive(Debug, Clone)]
68    pub struct Settings: u8 {
69        /// Trace requests (only request kind, e.g. `send_message`)
70        const TRACE_REQUESTS = 1;
71
72        /// Trace requests verbosely (with all parameters).
73        ///
74        /// Implies [`TRACE_REQUESTS`]
75        const TRACE_REQUESTS_VERBOSE = (1 << 1) | Self::TRACE_REQUESTS.bits();
76
77        /// Trace responses (only request kind, e.g. `send_message`)
78        const TRACE_RESPONSES = 1 << 2;
79
80        /// Trace responses verbosely (with full response).
81        ///
82        /// Implies [`TRACE_RESPONSES`]
83        const TRACE_RESPONSES_VERBOSE = (1 << 3) | Self::TRACE_RESPONSES.bits();
84
85        /// Trace everything.
86        ///
87        /// Implies [`TRACE_REQUESTS`] and [`TRACE_RESPONSES`].
88        const TRACE_EVERYTHING = Self::TRACE_REQUESTS.bits() | Self::TRACE_RESPONSES.bits();
89
90        /// Trace everything verbosely.
91        ///
92        /// Implies [`TRACE_REQUESTS_VERBOSE`] and [`TRACE_RESPONSES_VERBOSE`].
93        const TRACE_EVERYTHING_VERBOSE = Self::TRACE_REQUESTS_VERBOSE.bits() | Self::TRACE_RESPONSES_VERBOSE.bits();
94    }
95}
96
97macro_rules! fty {
98    ($T:ident) => {
99        TraceRequest<B::$T>
100    };
101}
102
103macro_rules! fwd_inner {
104    ($m:ident $this:ident ($($arg:ident : $T:ty),*)) => {
105        TraceRequest {
106            inner: $this.inner().$m($($arg),*),
107            settings: $this.settings.clone()
108        }
109    };
110}
111
112impl<B> Requester for Trace<B>
113where
114    B: Requester,
115{
116    type Err = B::Err;
117
118    requester_forward! {
119        get_me,
120        log_out,
121        close,
122        get_updates,
123        set_webhook,
124        delete_webhook,
125        get_webhook_info,
126        forward_message,
127        forward_messages,
128        copy_message,
129        copy_messages,
130        send_message,
131        send_photo,
132        send_audio,
133        send_document,
134        send_video,
135        send_animation,
136        send_voice,
137        send_video_note,
138        send_paid_media,
139        send_media_group,
140        send_location,
141        edit_message_live_location,
142        edit_message_live_location_inline,
143        stop_message_live_location,
144        stop_message_live_location_inline,
145        edit_message_checklist,
146        send_venue,
147        send_contact,
148        send_poll,
149        send_checklist,
150        send_dice,
151        send_chat_action,
152        set_message_reaction,
153        get_user_profile_photos,
154        set_user_emoji_status,
155        get_file,
156        kick_chat_member,
157        ban_chat_member,
158        unban_chat_member,
159        restrict_chat_member,
160        promote_chat_member,
161        set_chat_administrator_custom_title,
162        ban_chat_sender_chat,
163        unban_chat_sender_chat,
164        set_chat_permissions,
165        export_chat_invite_link,
166        create_chat_invite_link,
167        edit_chat_invite_link,
168        create_chat_subscription_invite_link,
169        edit_chat_subscription_invite_link,
170        revoke_chat_invite_link,
171        set_chat_photo,
172        delete_chat_photo,
173        set_chat_title,
174        set_chat_description,
175        pin_chat_message,
176        unpin_chat_message,
177        unpin_all_chat_messages,
178        leave_chat,
179        get_chat,
180        get_chat_administrators,
181        get_chat_members_count,
182        get_chat_member_count,
183        get_chat_member,
184        set_chat_sticker_set,
185        delete_chat_sticker_set,
186        get_forum_topic_icon_stickers,
187        create_forum_topic,
188        edit_forum_topic,
189        close_forum_topic,
190        reopen_forum_topic,
191        delete_forum_topic,
192        unpin_all_forum_topic_messages,
193        edit_general_forum_topic,
194        close_general_forum_topic,
195        reopen_general_forum_topic,
196        hide_general_forum_topic,
197        unhide_general_forum_topic,
198        unpin_all_general_forum_topic_messages,
199        answer_callback_query,
200        get_user_chat_boosts,
201        set_my_commands,
202        get_business_connection,
203        get_my_commands,
204        set_my_name,
205        get_my_name,
206        set_my_description,
207        get_my_description,
208        set_my_short_description,
209        get_my_short_description,
210        set_chat_menu_button,
211        get_chat_menu_button,
212        set_my_default_administrator_rights,
213        get_my_default_administrator_rights,
214        delete_my_commands,
215        answer_inline_query,
216        answer_web_app_query,
217        save_prepared_inline_message,
218        edit_message_text,
219        edit_message_text_inline,
220        edit_message_caption,
221        edit_message_caption_inline,
222        edit_message_media,
223        edit_message_media_inline,
224        edit_message_reply_markup,
225        edit_message_reply_markup_inline,
226        stop_poll,
227        delete_message,
228        delete_messages,
229        send_sticker,
230        get_sticker_set,
231        get_custom_emoji_stickers,
232        upload_sticker_file,
233        create_new_sticker_set,
234        add_sticker_to_set,
235        set_sticker_position_in_set,
236        delete_sticker_from_set,
237        replace_sticker_in_set,
238        set_sticker_set_thumbnail,
239        set_custom_emoji_sticker_set_thumbnail,
240        set_sticker_set_title,
241        delete_sticker_set,
242        set_sticker_emoji_list,
243        set_sticker_keywords,
244        set_sticker_mask_position,
245        get_available_gifts,
246        send_gift,
247        send_gift_chat,
248        gift_premium_subscription,
249        verify_user,
250        verify_chat,
251        remove_user_verification,
252        remove_chat_verification,
253        read_business_message,
254        delete_business_messages,
255        set_business_account_name,
256        set_business_account_username,
257        set_business_account_bio,
258        set_business_account_profile_photo,
259        remove_business_account_profile_photo,
260        set_business_account_gift_settings,
261        get_business_account_star_balance,
262        transfer_business_account_stars,
263        get_business_account_gifts,
264        convert_gift_to_stars,
265        upgrade_gift,
266        transfer_gift,
267        post_story,
268        edit_story,
269        delete_story,
270        send_invoice,
271        create_invoice_link,
272        answer_shipping_query,
273        answer_pre_checkout_query,
274        get_my_star_balance,
275        get_star_transactions,
276        refund_star_payment,
277        edit_user_star_subscription,
278        set_passport_data_errors,
279        send_game,
280        set_game_score,
281        set_game_score_inline,
282        get_game_high_scores,
283        approve_chat_join_request,
284        decline_chat_join_request
285        => fwd_inner, fty
286    }
287}
288
289#[must_use = "Requests are lazy and do nothing unless sent"]
290#[derive(Clone)]
291pub struct TraceRequest<R> {
292    inner: R,
293    settings: Settings,
294}
295
296impl<R> TraceRequest<R>
297where
298    R: Request,
299{
300    fn trace_request(&self)
301    where
302        R::Payload: Debug,
303    {
304        if self.settings.contains(Settings::TRACE_REQUESTS_VERBOSE) {
305            log::trace!(
306                "Sending `{}` request: {:?}",
307                <R::Payload as Payload>::NAME,
308                self.inner.payload_ref()
309            );
310        } else if self.settings.contains(Settings::TRACE_REQUESTS) {
311            log::trace!("Sending `{}` request", R::Payload::NAME);
312        }
313    }
314
315    fn trace_response_fn(&self) -> fn(&Result<Output<R>, R::Err>)
316    where
317        Output<R>: Debug,
318        R::Err: Debug,
319    {
320        if self.settings.contains(Settings::TRACE_RESPONSES_VERBOSE) {
321            |response| {
322                log::trace!("Got response from `{}` request: {:?}", R::Payload::NAME, response)
323            }
324        } else if self.settings.contains(Settings::TRACE_RESPONSES) {
325            |_| log::trace!("Got response from `{}` request", R::Payload::NAME)
326        } else {
327            |_| {}
328        }
329    }
330}
331
332impl<R> HasPayload for TraceRequest<R>
333where
334    R: HasPayload,
335{
336    type Payload = R::Payload;
337
338    fn payload_mut(&mut self) -> &mut Self::Payload {
339        self.inner.payload_mut()
340    }
341
342    fn payload_ref(&self) -> &Self::Payload {
343        self.inner.payload_ref()
344    }
345}
346
347impl<R> Request for TraceRequest<R>
348where
349    R: Request,
350    Output<R>: Debug,
351    R::Err: Debug,
352    R::Payload: Debug,
353{
354    type Err = R::Err;
355
356    type Send = Send<R::Send>;
357
358    type SendRef = Send<R::SendRef>;
359
360    fn send(self) -> Self::Send {
361        self.trace_request();
362
363        Send { trace_fn: self.trace_response_fn(), inner: self.inner.send() }
364    }
365
366    fn send_ref(&self) -> Self::SendRef {
367        self.trace_request();
368
369        Send { trace_fn: self.trace_response_fn(), inner: self.inner.send_ref() }
370    }
371}
372
373impl<R> IntoFuture for TraceRequest<R>
374where
375    R: Request,
376    Output<R>: Debug,
377    R::Err: Debug,
378    R::Payload: Debug,
379{
380    type Output = Result<Output<Self>, <Self as Request>::Err>;
381    type IntoFuture = <Self as Request>::Send;
382
383    fn into_future(self) -> Self::IntoFuture {
384        self.send()
385    }
386}
387
388#[pin_project::pin_project]
389pub struct Send<F>
390where
391    F: Future,
392{
393    trace_fn: fn(&F::Output),
394    #[pin]
395    inner: F,
396}
397
398impl<F> Future for Send<F>
399where
400    F: Future,
401{
402    type Output = F::Output;
403
404    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
405        let this = self.project();
406
407        let ret = ready!(this.inner.poll(cx));
408        (this.trace_fn)(&ret);
409        Poll::Ready(ret)
410    }
411}