Skip to main content

layer_mtproto/
session.rs

1// Copyright (c) Ankit Chaubey <ankitchaubey.dev@gmail.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4// NOTE:
5// The "Layer" project is no longer maintained or supported.
6// Its original purpose for personal SDK/APK experimentation and learning
7// has been fulfilled.
8//
9// Please use Ferogram instead:
10// https://github.com/ankit-chaubey/ferogram
11// Ferogram will receive future updates and development, although progress
12// may be slower.
13//
14// Ferogram is an async Telegram MTProto client library written in Rust.
15// Its implementation follows the behaviour of the official Telegram clients,
16// particularly Telegram Desktop and TDLib, and aims to provide a clean and
17// modern async interface for building Telegram clients and tools.
18
19//! MTProto client session state.
20
21use layer_tl_types::RemoteCall;
22
23use crate::message::{Message, MessageId};
24
25/// Tracks per-connection MTProto session state.
26///
27/// A `Session` is cheap to create and can be reset on reconnect.
28///
29/// # Example
30///
31/// ```rust
32/// use layer_mtproto::Session;
33/// use layer_tl_types::functions;
34///
35/// let mut session = Session::new();
36/// // let msg = session.pack(&my_request);
37/// // send(msg.to_plaintext_bytes()).await?;
38/// ```
39pub struct Session {
40    /// Monotonically increasing counter used to generate unique message IDs.
41    msg_counter: u32,
42    /// The sequence number for the next message.
43    /// Even for content-unrelated messages, odd for content-related (RPC calls).
44    seq_no: i32,
45}
46
47impl Session {
48    /// Create a fresh session.
49    pub fn new() -> Self {
50        Self {
51            msg_counter: 0,
52            seq_no: 0,
53        }
54    }
55
56    /// Allocate a new message ID.
57    pub fn next_msg_id(&mut self) -> MessageId {
58        self.msg_counter = self.msg_counter.wrapping_add(1);
59        MessageId::generate(self.msg_counter)
60    }
61
62    /// Return the next sequence number for a content-related message (RPC call).
63    ///
64    /// Increments by 2 after each call so that even slots remain available
65    /// for content-unrelated messages (acks, pings, etc.).
66    pub fn next_seq_no(&mut self) -> i32 {
67        let n = self.seq_no;
68        self.seq_no += 2;
69        n | 1 // odd = content-related
70    }
71
72    /// Return the next sequence number for a content-*un*related message.
73    pub fn next_seq_no_unrelated(&mut self) -> i32 {
74        let n = self.seq_no;
75        n & !1 // even = content-unrelated (don't increment)
76    }
77
78    /// Serialize an RPC function into a [`Message`] ready to send.
79    ///
80    /// The message body is just the TL-serialized `call`; the surrounding
81    /// transport framing (auth_key_id, etc.) is applied in [`Message::to_plaintext_bytes`].
82    pub fn pack<R: RemoteCall>(&mut self, call: &R) -> Message {
83        let id = self.next_msg_id();
84        let seq_no = self.next_seq_no();
85        let body = call.to_bytes();
86        Message::plaintext(id, seq_no, body)
87    }
88}
89
90impl Default for Session {
91    fn default() -> Self {
92        Self::new()
93    }
94}