Skip to main content

ronami_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 ronami_core::adaptors::trace > Sending `SendDice` request
24/// TRACE ronami_core::adaptors::trace > Got response from `SendDice` request
25/// TRACE ronami_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 ronami_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 ronami_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_message_draft,
132        send_photo,
133        send_audio,
134        send_document,
135        send_video,
136        send_animation,
137        send_voice,
138        send_video_note,
139        send_paid_media,
140        send_media_group,
141        send_location,
142        edit_message_live_location,
143        edit_message_live_location_inline,
144        stop_message_live_location,
145        stop_message_live_location_inline,
146        edit_message_checklist,
147        send_venue,
148        send_contact,
149        send_poll,
150        send_checklist,
151        send_dice,
152        send_chat_action,
153        set_message_reaction,
154        get_user_profile_photos,
155        get_user_profile_audios,
156        set_user_emoji_status,
157        get_file,
158        kick_chat_member,
159        ban_chat_member,
160        unban_chat_member,
161        restrict_chat_member,
162        promote_chat_member,
163        set_chat_administrator_custom_title,
164        set_chat_member_tag,
165        ban_chat_sender_chat,
166        unban_chat_sender_chat,
167        set_chat_permissions,
168        export_chat_invite_link,
169        create_chat_invite_link,
170        edit_chat_invite_link,
171        create_chat_subscription_invite_link,
172        edit_chat_subscription_invite_link,
173        revoke_chat_invite_link,
174        set_chat_photo,
175        delete_chat_photo,
176        set_chat_title,
177        set_chat_description,
178        pin_chat_message,
179        unpin_chat_message,
180        unpin_all_chat_messages,
181        leave_chat,
182        get_chat,
183        get_chat_administrators,
184        get_chat_members_count,
185        get_chat_member_count,
186        get_chat_member,
187        set_chat_sticker_set,
188        delete_chat_sticker_set,
189        get_forum_topic_icon_stickers,
190        create_forum_topic,
191        edit_forum_topic,
192        close_forum_topic,
193        reopen_forum_topic,
194        delete_forum_topic,
195        unpin_all_forum_topic_messages,
196        edit_general_forum_topic,
197        close_general_forum_topic,
198        reopen_general_forum_topic,
199        hide_general_forum_topic,
200        unhide_general_forum_topic,
201        unpin_all_general_forum_topic_messages,
202        answer_callback_query,
203        get_user_chat_boosts,
204        set_my_commands,
205        get_business_connection,
206        get_my_commands,
207        set_my_name,
208        get_my_name,
209        set_my_description,
210        get_my_description,
211        set_my_short_description,
212        get_my_short_description,
213        set_my_profile_photo,
214        remove_my_profile_photo,
215        send_live_photo,
216        get_user_personal_chat_messages,
217        answer_guest_query,
218        get_managed_bot_access_settings,
219        set_managed_bot_access_settings,
220        delete_message_reaction,
221        delete_all_message_reactions,
222        get_managed_bot_token,
223        replace_managed_bot_token,
224        save_prepared_keyboard_button,
225        set_chat_menu_button,
226        get_chat_menu_button,
227        set_my_default_administrator_rights,
228        get_my_default_administrator_rights,
229        delete_my_commands,
230        answer_inline_query,
231        answer_web_app_query,
232        save_prepared_inline_message,
233        edit_message_text,
234        edit_message_text_inline,
235        edit_message_caption,
236        edit_message_caption_inline,
237        edit_message_media,
238        edit_message_media_inline,
239        edit_message_reply_markup,
240        edit_message_reply_markup_inline,
241        stop_poll,
242        approve_suggested_post,
243        decline_suggested_post,
244        delete_message,
245        delete_messages,
246        send_sticker,
247        get_sticker_set,
248        get_custom_emoji_stickers,
249        upload_sticker_file,
250        create_new_sticker_set,
251        add_sticker_to_set,
252        set_sticker_position_in_set,
253        delete_sticker_from_set,
254        replace_sticker_in_set,
255        set_sticker_set_thumbnail,
256        set_custom_emoji_sticker_set_thumbnail,
257        set_sticker_set_title,
258        delete_sticker_set,
259        set_sticker_emoji_list,
260        set_sticker_keywords,
261        set_sticker_mask_position,
262        get_available_gifts,
263        send_gift,
264        send_gift_chat,
265        gift_premium_subscription,
266        verify_user,
267        verify_chat,
268        remove_user_verification,
269        remove_chat_verification,
270        read_business_message,
271        delete_business_messages,
272        set_business_account_name,
273        set_business_account_username,
274        set_business_account_bio,
275        set_business_account_profile_photo,
276        remove_business_account_profile_photo,
277        set_business_account_gift_settings,
278        get_business_account_star_balance,
279        transfer_business_account_stars,
280        get_business_account_gifts,
281        get_user_gifts,
282        get_chat_gifts,
283        convert_gift_to_stars,
284        upgrade_gift,
285        transfer_gift,
286        post_story,
287        repost_story,
288        edit_story,
289        delete_story,
290        send_invoice,
291        create_invoice_link,
292        answer_shipping_query,
293        answer_pre_checkout_query,
294        get_my_star_balance,
295        get_star_transactions,
296        refund_star_payment,
297        edit_user_star_subscription,
298        set_passport_data_errors,
299        send_game,
300        set_game_score,
301        set_game_score_inline,
302        get_game_high_scores,
303        approve_chat_join_request,
304        decline_chat_join_request
305        => fwd_inner, fty
306    }
307}
308
309#[must_use = "Requests are lazy and do nothing unless sent"]
310#[derive(Clone)]
311pub struct TraceRequest<R> {
312    inner: R,
313    settings: Settings,
314}
315
316impl<R> TraceRequest<R>
317where
318    R: Request,
319{
320    fn trace_request(&self)
321    where
322        R::Payload: Debug,
323    {
324        if self.settings.contains(Settings::TRACE_REQUESTS_VERBOSE) {
325            log::trace!(
326                "Sending `{}` request: {:?}",
327                <R::Payload as Payload>::NAME,
328                self.inner.payload_ref()
329            );
330        } else if self.settings.contains(Settings::TRACE_REQUESTS) {
331            log::trace!("Sending `{}` request", R::Payload::NAME);
332        }
333    }
334
335    fn trace_response_fn(&self) -> fn(&Result<Output<R>, R::Err>)
336    where
337        Output<R>: Debug,
338        R::Err: Debug,
339    {
340        if self.settings.contains(Settings::TRACE_RESPONSES_VERBOSE) {
341            |response| {
342                log::trace!("Got response from `{}` request: {:?}", R::Payload::NAME, response)
343            }
344        } else if self.settings.contains(Settings::TRACE_RESPONSES) {
345            |_| log::trace!("Got response from `{}` request", R::Payload::NAME)
346        } else {
347            |_| {}
348        }
349    }
350}
351
352impl<R> HasPayload for TraceRequest<R>
353where
354    R: HasPayload,
355{
356    type Payload = R::Payload;
357
358    fn payload_mut(&mut self) -> &mut Self::Payload {
359        self.inner.payload_mut()
360    }
361
362    fn payload_ref(&self) -> &Self::Payload {
363        self.inner.payload_ref()
364    }
365}
366
367impl<R> Request for TraceRequest<R>
368where
369    R: Request,
370    Output<R>: Debug,
371    R::Err: Debug,
372    R::Payload: Debug,
373{
374    type Err = R::Err;
375
376    type Send = Send<R::Send>;
377
378    type SendRef = Send<R::SendRef>;
379
380    fn send(self) -> Self::Send {
381        self.trace_request();
382
383        Send { trace_fn: self.trace_response_fn(), inner: self.inner.send() }
384    }
385
386    fn send_ref(&self) -> Self::SendRef {
387        self.trace_request();
388
389        Send { trace_fn: self.trace_response_fn(), inner: self.inner.send_ref() }
390    }
391}
392
393impl<R> IntoFuture for TraceRequest<R>
394where
395    R: Request,
396    Output<R>: Debug,
397    R::Err: Debug,
398    R::Payload: Debug,
399{
400    type Output = Result<Output<Self>, <Self as Request>::Err>;
401    type IntoFuture = <Self as Request>::Send;
402
403    fn into_future(self) -> Self::IntoFuture {
404        self.send()
405    }
406}
407
408#[pin_project::pin_project]
409pub struct Send<F>
410where
411    F: Future,
412{
413    trace_fn: fn(&F::Output),
414    #[pin]
415    inner: F,
416}
417
418impl<F> Future for Send<F>
419where
420    F: Future,
421{
422    type Output = F::Output;
423
424    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
425        let this = self.project();
426
427        let ret = ready!(this.inner.poll(cx));
428        (this.trace_fn)(&ret);
429        Poll::Ready(ret)
430    }
431}