ferogram_mtproto/session.rs
1// Copyright (c) Ankit Chaubey <ankitchaubey.dev@gmail.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3//
4// ferogram: async Telegram MTProto client in Rust
5// https://github.com/ankit-chaubey/ferogram
6//
7// If you use or modify this code, keep this notice at the top of your file
8// and include the LICENSE-MIT or LICENSE-APACHE file from this repository:
9// https://github.com/ankit-chaubey/ferogram
10
11use ferogram_tl_types::RemoteCall;
12
13use crate::message::{Message, MessageId};
14
15/// Tracks per-connection MTProto session state.
16///
17/// A `Session` is cheap to create and can be reset on reconnect.
18///
19/// # Example
20///
21/// ```rust
22/// use ferogram_mtproto::Session;
23/// use ferogram_tl_types::functions;
24///
25/// let mut session = Session::new();
26/// // let msg = session.pack(&my_request);
27/// // send(msg.to_plaintext_bytes()).await?;
28/// ```
29pub struct Session {
30 /// Monotonically increasing counter used to generate unique message IDs.
31 msg_counter: u32,
32 /// The sequence number for the next message.
33 /// Even for content-unrelated messages, odd for content-related (RPC calls).
34 seq_no: i32,
35}
36
37impl Session {
38 /// Create a fresh session.
39 pub fn new() -> Self {
40 Self {
41 msg_counter: 0,
42 seq_no: 0,
43 }
44 }
45
46 /// Allocate a new message ID.
47 pub fn next_msg_id(&mut self) -> MessageId {
48 self.msg_counter = self.msg_counter.wrapping_add(1);
49 MessageId::generate(self.msg_counter)
50 }
51
52 /// Return the next sequence number for a content-related message (RPC call).
53 ///
54 /// Increments by 2 after each call so that even slots remain available
55 /// for content-unrelated messages (acks, pings, etc.).
56 pub fn next_seq_no(&mut self) -> i32 {
57 let n = self.seq_no;
58 self.seq_no += 2;
59 n | 1 // odd = content-related
60 }
61
62 /// Return the next sequence number for a content-*un*related message.
63 pub fn next_seq_no_unrelated(&mut self) -> i32 {
64 let n = self.seq_no;
65 n & !1 // even = content-unrelated (don't increment)
66 }
67
68 /// Serialize an RPC function into a [`Message`] ready to send.
69 ///
70 /// The message body is just the TL-serialized `call`; the surrounding
71 /// transport framing (auth_key_id, etc.) is applied in [`Message::to_plaintext_bytes`].
72 pub fn pack<R: RemoteCall>(&mut self, call: &R) -> Message {
73 let id = self.next_msg_id();
74 let seq_no = self.next_seq_no();
75 let body = call.to_bytes();
76 Message::plaintext(id, seq_no, body)
77 }
78}
79
80impl Default for Session {
81 fn default() -> Self {
82 Self::new()
83 }
84}