td_client/client.rs
1//! Detached request client for concurrent Telegram operations.
2//!
3//! Obtain a cloneable [`Client`] from [`Session::client`](crate::session::Session::client).
4//! Clients issue requests concurrently without borrowing update consumption.
5//!
6//! Direct function calls use [`Client::send`]. Normal message sends with terminal
7//! delivery tracking use [`Client::track`] and [`Client::track_all`]. File
8//! downloads use [`Client::download`].
9
10use std::sync::{Arc, Weak};
11
12use td_types::traits::Function;
13
14use crate::connection::Connection;
15use crate::error::{Error, Result};
16
17/// A cloneable, non-owning capability for requests to one [`Session`](crate::session::Session).
18///
19/// Clones refer to the same session and may submit requests concurrently. They
20/// cannot receive updates or initiate graceful closure. Keeping a client alive
21/// does not keep the session operational: requests after owner loss or closure
22/// admission fail with [`Error::Disconnected`].
23///
24/// Methods are asynchronous and do not submit until their futures are polled.
25#[derive(Debug, Clone)]
26pub struct Client(pub(crate) Weak<Connection>);
27
28impl Client {
29 /// Sends a generated function and returns its direct `TDLib` response.
30 ///
31 /// `F::Return` is supplied by `td-types::traits::Function`. This is the general
32 /// entry point for the generated API, including getters and message edits.
33 /// For normal sends, the direct response can still describe a pending temporary
34 /// message; use [`track`](Self::track) or [`track_all`](Self::track_all) to
35 /// wait for terminal outcomes.
36 ///
37 /// # Errors
38 ///
39 /// Returns [`Error::Json`] for serialization/decoding failures, [`Error::Td`]
40 /// for `TDLib` error responses, and [`Error::Disconnected`] when the session
41 /// cannot accept the request or its reply is abandoned during teardown.
42 ///
43 /// # Cancellation
44 ///
45 /// Dropping this future stops waiting, not the submitted native request.
46 /// A timeout therefore does not establish that a side effect failed to occur.
47 /// Retries and native cancellation are application decisions.
48 ///
49 /// # Examples
50 ///
51 /// ```no_run
52 /// # use td_client::Client;
53 /// # use td_client::Result;
54 /// use td_types::{enums::User, fns};
55 ///
56 /// # async fn identify(client: &Client) -> Result {
57 /// let User::user(user) = client.send(&fns::getMe {}).await?;
58 /// println!("{}", user.first_name);
59 /// # Ok(())
60 /// # }
61 /// ```
62 pub async fn send<F: Function>(&self, request: &F) -> Result<F::Return> {
63 self.connection()?.request(request).await
64 }
65
66 pub(crate) fn connection(&self) -> Result<Arc<Connection>> {
67 self.0.upgrade().ok_or(Error::Disconnected)
68 }
69}