Skip to main content

grammers_session/
peer.rs

1// Copyright 2020 - developers of the `grammers` project.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::{
10    fmt,
11    ops::{Deref as _, RangeInclusive},
12};
13
14use grammers_tl_types as tl;
15
16/// A compact peer identifier.
17/// ```
18/// use std::mem::size_of;
19/// assert_eq!(size_of::<grammers_session::types::PeerId>(), size_of::<i64>());
20/// ```
21/// The [`PeerInfo`] cached by the session for this `PeerId` may be retrieved via [`crate::Session::peer`].
22///
23/// The internal representation uses the Bot API Dialog ID format to
24/// bit-pack both the peer's true identifier and type in a single integer.
25///
26/// Internally, arbitrary values outside the valid range of Bot API Dialog ID
27/// may be used to represent special peer identifiers.
28#[repr(transparent)]
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31pub struct PeerId(i64);
32
33/// Witness to the session's authority from Telegram to interact with a peer.
34///
35/// If Telegram deems the session to already have such authority, the session may
36/// be allowed to present [`PeerAuth::default`] instead of this witness. This can
37/// happen when the logged-in user is a bot account, or, for user accounts, when
38/// the peer being interacted with is one of its contacts.
39#[repr(transparent)]
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
42pub struct PeerAuth(i64);
43
44/// Ocap-style reference to a peer object, a peer object capability, bundling the identity
45/// of a peer (its [`PeerId`]) with authority over it (as [`PeerAuth`]), to allow fluent use.
46///
47/// This type implements conversion to [`tl::enums::InputPeer`] and derivatives.
48#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50pub struct PeerRef {
51    /// The peer identity.
52    pub id: PeerId,
53    /// The authority bound to both the sibling identity and the session of the logged-in user.
54    pub auth: PeerAuth,
55}
56
57/// [`PeerId`]'s kind.
58///
59/// The `PeerId` bitpacks this information for size reasons.
60#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
61#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
62pub enum PeerKind {
63    /// The peer identity belongs to a [`tl::enums::User`] or the self-user without knowing its identifier.
64    User,
65    /// The peer identity belongs to a [`tl::types::Chat`] or one of its derivatives.
66    Chat,
67    /// The peer identity belongs to a [`tl::types::Channel`] or one of its derivatives.
68    Channel,
69}
70
71/// An exploded peer reference along with any known useful information about the peer.
72#[derive(Clone, Debug, PartialEq, Eq)]
73#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
74pub enum PeerInfo {
75    User {
76        /// Bare user identifier.
77        ///
78        /// Despite being `i64`, Telegram only uses strictly positive values.
79        id: i64,
80        /// Non-ambient authority bound to both the user itself and the session.
81        auth: Option<PeerAuth>,
82        /// Whether this user represents a bot or not.
83        bot: Option<bool>,
84        /// Whether this user represents the logged-in user authorized by this session or not.
85        is_self: Option<bool>,
86    },
87    Chat {
88        /// Bare chat identifier.
89        ///
90        /// Note that the HTTP Bot API negates this identifier to signal that it is a chat,
91        /// but the true value used by Telegram's API is always strictly-positive.
92        id: i64,
93    },
94    Channel {
95        /// Bare channel identifier.
96        ///
97        /// Note that the HTTP Bot API prefixes this identifier with `-100` to signal that it is a channel,
98        /// but the true value used by Telegram's API is always strictly-positive.
99        id: i64,
100        /// Non-ambient authority bound to both the user itself and the session.
101        auth: Option<PeerAuth>,
102        /// Channel kind, useful to determine what the possible permissions on it are.
103        kind: Option<ChannelKind>,
104    },
105}
106
107/// Additional information about a [`PeerInfo::Channel`].
108///
109/// A non-zero enum,
110/// to make working with `Option<ChannelKind>`
111/// and `Result<ChannelKind, ()>`
112/// slightly more performant.
113/// (See [`mod@core::option`]'s documentation about the "null pointer optimization".)
114#[derive(Clone, Copy, Debug, PartialEq, Eq)]
115#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
116pub enum ChannelKind {
117    /// Value used for a channel with its [`tl::types::Channel::broadcast`] flag set to `true`.
118    Broadcast = 1,
119    /// Value used for a channel with its [`tl::types::Channel::megagroup`] flag set to `true`.
120    Megagroup,
121    /// Value used for a channel with its [`tl::types::Channel::gigagroup`] flag set to `true`.
122    Gigagroup,
123}
124
125/// Sentinel value used to represent the self-user
126/// when its true `PeerId` is unknown.
127///
128/// Per <https://core.telegram.org/api/bots/ids>:
129/// > a bot API dialog ID ranges from -4000000000000 to 1099511627775
130///
131/// This value is not intended to be visible or persisted,
132/// so it can be changed as needed in the future.
133const SELF_USER_ID: PeerId = PeerId(1 << 40);
134
135/// Sentinel value used to represent empty chats.
136///
137/// Per <https://core.telegram.org/api/bots/ids>:
138/// > \[…] transformed range for bot API chat dialog IDs is -999999999999 to -1 inclusively
139/// >
140/// > \[…] transformed range for bot API channel dialog IDs is -1997852516352 to -1000000000001 inclusively
141///
142/// `chat_id` parameters are in Telegram's API use the bare identifier,
143/// so there's no empty constructor,
144/// but it can be mimicked by picking the value in the correct range hole.
145/// This value is closer to "channel with ID 0" than "chat with ID 0",
146/// but there's no distinct `-0` integer,
147/// and channels have a proper constructor for empty already.
148const EMPTY_CHAT_ID: i64 = -1000000000000;
149
150/// https://core.telegram.org/api/bots/ids#user-ids
151const USER_ID_RANGE: RangeInclusive<i64> = 1..=0xffffffffff;
152/// https://core.telegram.org/api/bots/ids#chat-ids
153const CHAT_ID_RANGE: RangeInclusive<i64> = 1..=999999999999;
154/// https://core.telegram.org/api/bots/ids#supergroup-channel-ids
155const SUPERGROUP_AND_CHANNEL_ID_RANGE: RangeInclusive<i64> = 1..=997852516352;
156/// https://core.telegram.org/api/bots/ids#monoforum-ids
157const MONOFORUM_ID_RANGE: RangeInclusive<i64> = 1002147483649..=3000000000000;
158
159impl PeerId {
160    /// Creates a peer identity for the currently-logged-in user or bot account.
161    ///
162    /// Internally, this will use a special sentinel value outside of any valid Bot API Dialog ID range.
163    pub fn self_user() -> Self {
164        SELF_USER_ID
165    }
166
167    /// Creates a peer identity for a user or bot account.
168    pub fn user(id: i64) -> Option<Self> {
169        USER_ID_RANGE.contains(&id).then_some(Self(id))
170    }
171
172    /// Creates a peer identity for a small group chat.
173    pub fn chat(id: i64) -> Option<Self> {
174        CHAT_ID_RANGE.contains(&id).then_some(Self(-id))
175    }
176
177    /// Creates a peer identity for a broadcast channel, megagroup, gigagroup or monoforum.
178    pub fn channel(id: i64) -> Option<Self> {
179        (SUPERGROUP_AND_CHANNEL_ID_RANGE.contains(&id) || MONOFORUM_ID_RANGE.contains(&id))
180            .then_some(Self(-(1000000000000 + id)))
181    }
182
183    /// Creates a peer identity from a [Bot API dialog ID](https://core.telegram.org/api/bots/ids).
184    pub fn from_bot_api_dialog_id(id: i64) -> Option<Self> {
185        if (1 <= id && id <= 0xffffffffff)
186            || (-999999999999 <= id && id <= -1)
187            || (-1997852516352 <= id && id <= -1000000000001
188                || (-4000000000000 <= id && id <= -2002147483649))
189        {
190            Some(Self(id))
191        } else {
192            None
193        }
194    }
195
196    /// Creates a peer identity for a user or bot account.
197    /// Panics if the ID is out of the valid range.
198    #[doc(hidden)]
199    pub fn user_unchecked(id: i64) -> Self {
200        debug_assert!(USER_ID_RANGE.contains(&id), "user ID out of range");
201        Self(id)
202    }
203
204    /// Creates a peer identity for a small group chat.
205    /// Panics if the ID is out of the valid range.
206    #[doc(hidden)]
207    pub fn chat_unchecked(id: i64) -> Self {
208        debug_assert!(CHAT_ID_RANGE.contains(&id), "chat ID out of range");
209        Self(-id)
210    }
211
212    /// Creates a peer identity for a broadcast channel, megagroup, gigagroup or monoforum.
213    /// Panics if the ID is out of the valid range.
214    #[doc(hidden)]
215    pub fn channel_unchecked(id: i64) -> Self {
216        debug_assert!(
217            (SUPERGROUP_AND_CHANNEL_ID_RANGE.contains(&id) || MONOFORUM_ID_RANGE.contains(&id)),
218            "channel ID out of range"
219        );
220        Self(-(1000000000000 + id))
221    }
222
223    /// Peer kind.
224    pub fn kind(self) -> PeerKind {
225        if 1 <= self.0 && self.0 <= 0xffffffffff || self.0 == SELF_USER_ID.0 {
226            PeerKind::User
227        } else if -999999999999 <= self.0 && self.0 <= -1 {
228            PeerKind::Chat
229        } else if -1997852516352 <= self.0 && self.0 <= -1000000000001
230            || (-4000000000000 <= self.0 && self.0 <= -2002147483649)
231        {
232            PeerKind::Channel
233        } else {
234            unreachable!()
235        }
236    }
237
238    /// Returns the identity using the [Bot API dialog ID](https://core.telegram.org/api/bots/ids)
239    /// format, which has the [`PeerKind`] embedded.
240    ///
241    /// Returns `None` if the peer was constructed without providing an identifier.
242    /// For now, this can only happen if `self` represents the [`Self::self_user`].
243    pub fn bot_api_dialog_id(self) -> Option<i64> {
244        (self.0 != SELF_USER_ID.0).then_some(self.0)
245    }
246
247    /// Returns the identity using the [Bot API dialog ID](https://core.telegram.org/api/bots/ids)
248    /// format, which has the [`PeerKind`] embedded.
249    #[doc(hidden)]
250    pub fn bot_api_dialog_id_unchecked(self) -> i64 {
251        debug_assert!(self.0 != SELF_USER_ID.0);
252        self.0
253    }
254
255    /// Bare peer identifier without the [`PeerKind`] tag embedded, as used by Telegram's MTProto API.
256    ///
257    /// Returns `None` if the peer was constructed without providing an identifier.
258    /// For now, this can only happen if `self` represents the [`Self::self_user`].
259    pub fn bare_id(self) -> Option<i64> {
260        if self.0 == SELF_USER_ID.0 {
261            return None;
262        }
263        Some(match self.kind() {
264            PeerKind::User => self.0,
265            PeerKind::Chat => -self.0,
266            PeerKind::Channel => -self.0 - 1000000000000,
267        })
268    }
269
270    /// Bare peer identifier without the [`PeerKind`] tag embedded, as used by Telegram's MTProto API.
271    #[doc(hidden)]
272    pub fn bare_id_unchecked(self) -> i64 {
273        debug_assert!(self.0 != SELF_USER_ID.0);
274        match self.kind() {
275            PeerKind::User => self.0,
276            PeerKind::Chat => -self.0,
277            PeerKind::Channel => -self.0 - 1000000000000,
278        }
279    }
280
281    /// Returns the `PeerRef` for this identifier with ambient authority (i.e. [`PeerAuth::default`]).
282    ///
283    /// This is useful when [`crate::Session::peer_ref`] returns `None` due to not having any cached.
284    ///
285    /// # Examples
286    ///
287    /// ```
288    /// # async fn f(session: &grammers_session::ErasedSession, peer_id: grammers_session::types::PeerId) -> Result<(), Box<dyn std::error::Error>> {
289    /// let peer_ref = session
290    ///     .peer_ref(peer_id)
291    ///     .await
292    ///     .unwrap()
293    ///     .unwrap_or(peer_id.to_ambient_ref());
294    ///
295    /// // Can try using the `peer_ref` to e.g. send messages now, even if no auth was found in cache,
296    /// // if Telegram deems you have permission to do so (e.g. you're a bot or peer is your contact).
297    /// # Ok(())
298    /// # }
299    /// ```
300    pub fn to_ambient_ref(self) -> PeerRef {
301        PeerRef {
302            id: self,
303            auth: PeerAuth::default(),
304        }
305    }
306}
307
308impl PeerAuth {
309    /// Construct a new peer authentication using Telegram's `access_hash` value.
310    pub fn from_hash(access_hash: i64) -> Self {
311        PeerAuth(access_hash)
312    }
313
314    /// Grants access to the internal access hash.
315    pub fn hash(self) -> i64 {
316        self.0
317    }
318}
319
320impl Default for PeerAuth {
321    /// Returns the ambient authority to authorize peers only when Telegram considers it valid.
322    ///
323    /// The internal representation uses `0` to signal the ambient authority,
324    /// although this might happen to be the actual witness used by some peers.
325    fn default() -> Self {
326        Self(0)
327    }
328}
329
330impl PeerInfo {
331    /// Returns the `PeerId` represented by this info.
332    pub fn id(&self) -> PeerId {
333        match self {
334            PeerInfo::User { id, .. } => PeerId::user_unchecked(*id),
335            PeerInfo::Chat { id } => PeerId::chat_unchecked(*id),
336            PeerInfo::Channel { id, .. } => PeerId::channel_unchecked(*id),
337        }
338    }
339
340    /// Returns the `PeerAuth` stored in this info.
341    pub fn auth(&self) -> Option<PeerAuth> {
342        match self {
343            PeerInfo::User { auth, .. } => *auth,
344            PeerInfo::Chat { .. } => Some(PeerAuth::default()),
345            PeerInfo::Channel { auth, .. } => *auth,
346        }
347    }
348
349    /// Updates self with any new information contained in the other info.
350    ///
351    /// This is equivalent to performing an "or-assign" operation on all the fields.
352    ///
353    /// Returns `true` if the other info matches in type and identifier.
354    /// If they do not match, no fields will be updated, and `false` is returned instead.
355    pub fn extend_info(&mut self, other: &PeerInfo) -> bool {
356        match (self, &other) {
357            (
358                PeerInfo::User {
359                    id,
360                    auth,
361                    bot,
362                    is_self,
363                },
364                PeerInfo::User {
365                    id: new_id,
366                    auth: new_auth,
367                    bot: new_bot,
368                    is_self: new_self,
369                },
370            ) if id == new_id => {
371                *auth = auth.or(*new_auth);
372                *bot = bot.or(*new_bot);
373                *is_self = is_self.or(*new_self);
374                true
375            }
376            (PeerInfo::Chat { id }, PeerInfo::Chat { id: new_id }) if id == new_id => true,
377            (
378                PeerInfo::Channel { id, auth, kind },
379                PeerInfo::Channel {
380                    id: new_id,
381                    auth: new_auth,
382                    kind: new_kind,
383                },
384            ) if id == new_id => {
385                *auth = auth.or(*new_auth);
386                *kind = kind.or(*new_kind);
387                true
388            }
389            _ => false,
390        }
391    }
392}
393
394impl fmt::Display for PeerId {
395    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396        match self.bot_api_dialog_id() {
397            Some(id) => id.fmt(f),
398            None => f.write_str("<self-user>"),
399        }
400    }
401}
402
403impl From<tl::enums::Peer> for PeerId {
404    #[inline]
405    fn from(peer: tl::enums::Peer) -> Self {
406        <Self as From<&tl::enums::Peer>>::from(&peer)
407    }
408}
409impl<'a> From<&'a tl::enums::Peer> for PeerId {
410    fn from(peer: &'a tl::enums::Peer) -> Self {
411        use tl::enums::Peer;
412        match peer {
413            Peer::User(user) => Self::from(user),
414            Peer::Chat(chat) => Self::from(chat),
415            Peer::Channel(channel) => Self::from(channel),
416        }
417    }
418}
419
420impl From<tl::types::PeerUser> for PeerId {
421    #[inline]
422    fn from(user: tl::types::PeerUser) -> Self {
423        <Self as From<&tl::types::PeerUser>>::from(&user)
424    }
425}
426impl<'a> From<&'a tl::types::PeerUser> for PeerId {
427    fn from(user: &'a tl::types::PeerUser) -> Self {
428        Self::user_unchecked(user.user_id)
429    }
430}
431
432impl From<tl::types::PeerChat> for PeerId {
433    #[inline]
434    fn from(user: tl::types::PeerChat) -> Self {
435        <Self as From<&tl::types::PeerChat>>::from(&user)
436    }
437}
438impl<'a> From<&'a tl::types::PeerChat> for PeerId {
439    fn from(chat: &'a tl::types::PeerChat) -> Self {
440        Self::chat_unchecked(chat.chat_id)
441    }
442}
443
444impl From<tl::types::PeerChannel> for PeerId {
445    #[inline]
446    fn from(channel: tl::types::PeerChannel) -> Self {
447        <Self as From<&tl::types::PeerChannel>>::from(&channel)
448    }
449}
450impl<'a> From<&'a tl::types::PeerChannel> for PeerId {
451    fn from(channel: &'a tl::types::PeerChannel) -> Self {
452        Self::channel_unchecked(channel.channel_id)
453    }
454}
455
456impl From<tl::enums::InputPeer> for PeerRef {
457    #[inline]
458    fn from(peer: tl::enums::InputPeer) -> Self {
459        <Self as From<&tl::enums::InputPeer>>::from(&peer)
460    }
461}
462impl<'a> From<&'a tl::enums::InputPeer> for PeerRef {
463    fn from(peer: &'a tl::enums::InputPeer) -> Self {
464        use tl::{enums::InputPeer, types::InputPeerSelf};
465        match peer {
466            InputPeer::Empty => panic!("InputPeer::Empty cannot be converted to any Peer"),
467            InputPeer::PeerSelf => <Self as From<&'a _>>::from(&InputPeerSelf {}),
468            InputPeer::User(user) => <Self as From<&'a _>>::from(user),
469            InputPeer::Chat(chat) => <Self as From<&'a _>>::from(chat),
470            InputPeer::Channel(channel) => <Self as From<&'a _>>::from(channel),
471            InputPeer::UserFromMessage(user) => <Self as From<&'a _>>::from(user.deref()),
472            InputPeer::ChannelFromMessage(channel) => <Self as From<&'a _>>::from(channel.deref()),
473        }
474    }
475}
476
477impl From<tl::types::InputPeerSelf> for PeerRef {
478    #[inline]
479    fn from(self_user: tl::types::InputPeerSelf) -> Self {
480        <Self as From<&tl::types::InputPeerSelf>>::from(&self_user)
481    }
482}
483impl<'a> From<&'a tl::types::InputPeerSelf> for PeerRef {
484    fn from(self_user: &'a tl::types::InputPeerSelf) -> Self {
485        _ = self_user;
486        Self {
487            id: SELF_USER_ID,
488            auth: PeerAuth::default(),
489        }
490    }
491}
492
493impl From<tl::types::InputPeerUser> for PeerRef {
494    #[inline]
495    fn from(user: tl::types::InputPeerUser) -> Self {
496        <Self as From<&tl::types::InputPeerUser>>::from(&user)
497    }
498}
499impl<'a> From<&'a tl::types::InputPeerUser> for PeerRef {
500    fn from(user: &'a tl::types::InputPeerUser) -> Self {
501        Self {
502            id: PeerId::user_unchecked(user.user_id),
503            auth: PeerAuth::from_hash(user.access_hash),
504        }
505    }
506}
507
508impl From<tl::types::InputPeerChat> for PeerRef {
509    #[inline]
510    fn from(chat: tl::types::InputPeerChat) -> Self {
511        <Self as From<&tl::types::InputPeerChat>>::from(&chat)
512    }
513}
514impl<'a> From<&'a tl::types::InputPeerChat> for PeerRef {
515    fn from(chat: &'a tl::types::InputPeerChat) -> Self {
516        Self {
517            id: PeerId::chat_unchecked(chat.chat_id),
518            auth: PeerAuth::default(),
519        }
520    }
521}
522
523impl From<tl::types::InputPeerChannel> for PeerRef {
524    #[inline]
525    fn from(channel: tl::types::InputPeerChannel) -> Self {
526        <Self as From<&tl::types::InputPeerChannel>>::from(&channel)
527    }
528}
529impl<'a> From<&'a tl::types::InputPeerChannel> for PeerRef {
530    fn from(channel: &'a tl::types::InputPeerChannel) -> Self {
531        Self {
532            id: PeerId::channel_unchecked(channel.channel_id),
533            auth: PeerAuth::from_hash(channel.access_hash),
534        }
535    }
536}
537
538impl From<tl::types::InputPeerUserFromMessage> for PeerRef {
539    #[inline]
540    fn from(user: tl::types::InputPeerUserFromMessage) -> Self {
541        <Self as From<&tl::types::InputPeerUserFromMessage>>::from(&user)
542    }
543}
544impl<'a> From<&'a tl::types::InputPeerUserFromMessage> for PeerRef {
545    fn from(user: &'a tl::types::InputPeerUserFromMessage) -> Self {
546        // Not currently willing to make PeerRef significantly larger to accomodate for this uncommon type.
547        Self {
548            id: PeerId::user_unchecked(user.user_id),
549            auth: PeerAuth::default(),
550        }
551    }
552}
553
554impl From<tl::types::InputPeerChannelFromMessage> for PeerRef {
555    #[inline]
556    fn from(channel: tl::types::InputPeerChannelFromMessage) -> Self {
557        <Self as From<&tl::types::InputPeerChannelFromMessage>>::from(&channel)
558    }
559}
560impl<'a> From<&'a tl::types::InputPeerChannelFromMessage> for PeerRef {
561    fn from(channel: &'a tl::types::InputPeerChannelFromMessage) -> Self {
562        // Not currently willing to make PeerRef significantly larger to accomodate for this uncommon type.
563        Self {
564            id: PeerId::channel_unchecked(channel.channel_id),
565            auth: PeerAuth::default(),
566        }
567    }
568}
569
570impl From<tl::enums::User> for PeerRef {
571    #[inline]
572    fn from(user: tl::enums::User) -> Self {
573        <Self as From<&tl::enums::User>>::from(&user)
574    }
575}
576impl<'a> From<&'a tl::enums::User> for PeerRef {
577    fn from(user: &'a tl::enums::User) -> Self {
578        use tl::enums::User;
579        match user {
580            User::Empty(user) => <Self as From<&_>>::from(user),
581            User::User(user) => <Self as From<&_>>::from(user),
582        }
583    }
584}
585
586impl From<tl::types::UserEmpty> for PeerRef {
587    #[inline]
588    fn from(user: tl::types::UserEmpty) -> Self {
589        <Self as From<&tl::types::UserEmpty>>::from(&user)
590    }
591}
592impl<'a> From<&'a tl::types::UserEmpty> for PeerRef {
593    fn from(user: &'a tl::types::UserEmpty) -> Self {
594        Self {
595            id: PeerId::user_unchecked(user.id),
596            auth: PeerAuth::default(),
597        }
598    }
599}
600
601impl From<tl::types::User> for PeerRef {
602    #[inline]
603    fn from(user: tl::types::User) -> Self {
604        <Self as From<&tl::types::User>>::from(&user)
605    }
606}
607impl<'a> From<&'a tl::types::User> for PeerRef {
608    fn from(user: &'a tl::types::User) -> Self {
609        Self {
610            id: PeerId::user_unchecked(user.id),
611            auth: user
612                .access_hash
613                .map(PeerAuth::from_hash)
614                .unwrap_or(PeerAuth::default()),
615        }
616    }
617}
618
619impl From<tl::enums::Chat> for PeerRef {
620    #[inline]
621    fn from(chat: tl::enums::Chat) -> Self {
622        <Self as From<&tl::enums::Chat>>::from(&chat)
623    }
624}
625impl<'a> From<&'a tl::enums::Chat> for PeerRef {
626    fn from(chat: &'a tl::enums::Chat) -> Self {
627        use tl::enums::Chat;
628        match chat {
629            Chat::Empty(chat) => <Self as From<&_>>::from(chat),
630            Chat::Chat(chat) => <Self as From<&_>>::from(chat),
631            Chat::Forbidden(chat) => <Self as From<&_>>::from(chat),
632            Chat::Channel(channel) => <Self as From<&_>>::from(channel),
633            Chat::ChannelForbidden(channel) => <Self as From<&_>>::from(channel),
634        }
635    }
636}
637
638impl From<tl::types::ChatEmpty> for PeerRef {
639    #[inline]
640    fn from(chat: tl::types::ChatEmpty) -> Self {
641        <Self as From<&tl::types::ChatEmpty>>::from(&chat)
642    }
643}
644impl<'a> From<&'a tl::types::ChatEmpty> for PeerRef {
645    fn from(chat: &'a tl::types::ChatEmpty) -> Self {
646        Self {
647            id: PeerId::chat_unchecked(chat.id),
648            auth: PeerAuth::default(),
649        }
650    }
651}
652
653impl From<tl::types::Chat> for PeerRef {
654    #[inline]
655    fn from(chat: tl::types::Chat) -> Self {
656        <Self as From<&tl::types::Chat>>::from(&chat)
657    }
658}
659impl<'a> From<&'a tl::types::Chat> for PeerRef {
660    fn from(chat: &'a tl::types::Chat) -> Self {
661        Self {
662            id: PeerId::chat_unchecked(chat.id),
663            auth: PeerAuth::default(),
664        }
665    }
666}
667
668impl From<tl::types::ChatForbidden> for PeerRef {
669    #[inline]
670    fn from(chat: tl::types::ChatForbidden) -> Self {
671        <Self as From<&tl::types::ChatForbidden>>::from(&chat)
672    }
673}
674impl<'a> From<&'a tl::types::ChatForbidden> for PeerRef {
675    fn from(chat: &'a tl::types::ChatForbidden) -> Self {
676        Self {
677            id: PeerId::chat_unchecked(chat.id),
678            auth: PeerAuth::default(),
679        }
680    }
681}
682
683impl From<tl::types::Channel> for PeerRef {
684    #[inline]
685    fn from(channel: tl::types::Channel) -> Self {
686        <Self as From<&tl::types::Channel>>::from(&channel)
687    }
688}
689impl<'a> From<&'a tl::types::Channel> for PeerRef {
690    fn from(channel: &'a tl::types::Channel) -> Self {
691        Self {
692            id: PeerId::channel_unchecked(channel.id),
693            auth: channel
694                .access_hash
695                .map(PeerAuth::from_hash)
696                .unwrap_or(PeerAuth::default()),
697        }
698    }
699}
700
701impl From<tl::types::ChannelForbidden> for PeerRef {
702    #[inline]
703    fn from(channel: tl::types::ChannelForbidden) -> Self {
704        <Self as From<&tl::types::ChannelForbidden>>::from(&channel)
705    }
706}
707impl<'a> From<&'a tl::types::ChannelForbidden> for PeerRef {
708    fn from(channel: &'a tl::types::ChannelForbidden) -> Self {
709        Self {
710            id: PeerId::channel_unchecked(channel.id),
711            auth: PeerAuth::from_hash(channel.access_hash),
712        }
713    }
714}
715
716impl From<PeerId> for tl::enums::Peer {
717    #[inline]
718    fn from(peer: PeerId) -> Self {
719        <Self as From<&PeerId>>::from(&peer)
720    }
721}
722impl<'a> From<&'a PeerId> for tl::enums::Peer {
723    fn from(peer: &'a PeerId) -> Self {
724        match peer.kind() {
725            PeerKind::User => Self::User(tl::types::PeerUser {
726                user_id: peer.bare_id_unchecked(), // may have bogus data on release builds
727            }),
728            PeerKind::Chat => Self::Chat(tl::types::PeerChat {
729                chat_id: peer.bare_id_unchecked(),
730            }),
731            PeerKind::Channel => Self::Channel(tl::types::PeerChannel {
732                channel_id: peer.bare_id_unchecked(),
733            }),
734        }
735    }
736}
737
738/// Generate owned `From<PeerRef>` impls that forward to the `From<&PeerRef>` impl.
739macro_rules! forward_peer_ref_from {
740    ($($ty:ty),+ $(,)?) => {$(
741        impl From<PeerRef> for $ty {
742            #[inline]
743            fn from(peer: PeerRef) -> Self {
744                <Self as From<&PeerRef>>::from(&peer)
745            }
746        }
747    )+};
748}
749
750forward_peer_ref_from!(
751    tl::enums::InputPeer,
752    tl::enums::InputUser,
753    i64,
754    tl::enums::InputChannel,
755);
756
757impl<'a> From<&'a PeerRef> for tl::enums::InputPeer {
758    fn from(peer: &'a PeerRef) -> Self {
759        match peer.id.kind() {
760            PeerKind::User => match peer.id.bare_id() {
761                Some(user_id) => Self::User(tl::types::InputPeerUser {
762                    user_id,
763                    access_hash: peer.auth.hash(),
764                }),
765                None => Self::PeerSelf,
766            },
767            PeerKind::Chat => Self::Chat(tl::types::InputPeerChat {
768                chat_id: peer.id.bare_id_unchecked(),
769            }),
770            PeerKind::Channel => Self::Channel(tl::types::InputPeerChannel {
771                channel_id: peer.id.bare_id_unchecked(),
772                access_hash: peer.auth.hash(),
773            }),
774        }
775    }
776}
777
778impl<'a> From<&'a PeerRef> for tl::enums::InputUser {
779    fn from(peer: &'a PeerRef) -> Self {
780        match peer.id.kind() {
781            PeerKind::User => match peer.id.bare_id() {
782                Some(user_id) => Self::User(tl::types::InputUser {
783                    user_id,
784                    access_hash: peer.auth.hash(),
785                }),
786                None => Self::UserSelf,
787            },
788            PeerKind::Chat => Self::Empty,
789            PeerKind::Channel => Self::Empty,
790        }
791    }
792}
793
794impl<'a> From<&'a PeerRef> for i64 {
795    fn from(peer: &'a PeerRef) -> Self {
796        match peer.id.kind() {
797            PeerKind::User => EMPTY_CHAT_ID,
798            PeerKind::Chat => peer.id.bare_id_unchecked(),
799            PeerKind::Channel => EMPTY_CHAT_ID,
800        }
801    }
802}
803
804impl<'a> From<&'a PeerRef> for tl::enums::InputChannel {
805    fn from(peer: &'a PeerRef) -> Self {
806        match peer.id.kind() {
807            PeerKind::User => Self::Empty,
808            PeerKind::Chat => Self::Empty,
809            PeerKind::Channel => Self::Channel(tl::types::InputChannel {
810                channel_id: peer.id.bare_id_unchecked(),
811                access_hash: peer.auth.hash(),
812            }),
813        }
814    }
815}
816
817impl From<tl::enums::Chat> for PeerInfo {
818    #[inline]
819    fn from(chat: tl::enums::Chat) -> Self {
820        <Self as From<&tl::enums::Chat>>::from(&chat)
821    }
822}
823impl<'a> From<&'a tl::enums::Chat> for PeerInfo {
824    fn from(chat: &'a tl::enums::Chat) -> Self {
825        match chat {
826            tl::enums::Chat::Chat(chat) => <Self as From<&tl::types::Chat>>::from(&chat),
827            tl::enums::Chat::Empty(chat) => <Self as From<&tl::types::ChatEmpty>>::from(&chat),
828            tl::enums::Chat::Forbidden(chat) => {
829                <Self as From<&tl::types::ChatForbidden>>::from(&chat)
830            }
831            tl::enums::Chat::Channel(channel) => {
832                <Self as From<&tl::types::Channel>>::from(&channel)
833            }
834            tl::enums::Chat::ChannelForbidden(channel) => {
835                <Self as From<&tl::types::ChannelForbidden>>::from(&channel)
836            }
837        }
838    }
839}
840
841impl From<tl::enums::User> for PeerInfo {
842    #[inline]
843    fn from(user: tl::enums::User) -> Self {
844        <Self as From<&tl::enums::User>>::from(&user)
845    }
846}
847impl<'a> From<&'a tl::enums::User> for PeerInfo {
848    fn from(user: &'a tl::enums::User) -> Self {
849        match user {
850            tl::enums::User::User(user) => <Self as From<&tl::types::User>>::from(&user),
851            tl::enums::User::Empty(user) => <Self as From<&tl::types::UserEmpty>>::from(&user),
852        }
853    }
854}
855
856impl From<tl::types::User> for PeerInfo {
857    #[inline]
858    fn from(user: tl::types::User) -> Self {
859        <Self as From<&tl::types::User>>::from(&user)
860    }
861}
862impl<'a> From<&'a tl::types::User> for PeerInfo {
863    fn from(user: &'a tl::types::User) -> Self {
864        Self::User {
865            id: user.id,
866            auth: user.access_hash.map(PeerAuth).filter(|_| !user.min),
867            bot: Some(user.bot),
868            is_self: Some(user.is_self),
869        }
870    }
871}
872
873impl From<tl::types::UserEmpty> for PeerInfo {
874    #[inline]
875    fn from(user: tl::types::UserEmpty) -> Self {
876        <Self as From<&tl::types::UserEmpty>>::from(&user)
877    }
878}
879impl<'a> From<&'a tl::types::UserEmpty> for PeerInfo {
880    fn from(user: &'a tl::types::UserEmpty) -> Self {
881        Self::User {
882            id: user.id,
883            auth: None,
884            bot: None,
885            is_self: None,
886        }
887    }
888}
889
890impl From<tl::types::Chat> for PeerInfo {
891    #[inline]
892    fn from(chat: tl::types::Chat) -> Self {
893        <Self as From<&tl::types::Chat>>::from(&chat)
894    }
895}
896impl<'a> From<&'a tl::types::Chat> for PeerInfo {
897    fn from(chat: &'a tl::types::Chat) -> Self {
898        Self::Chat { id: chat.id }
899    }
900}
901
902impl From<tl::types::ChatEmpty> for PeerInfo {
903    #[inline]
904    fn from(chat: tl::types::ChatEmpty) -> Self {
905        <Self as From<&tl::types::ChatEmpty>>::from(&chat)
906    }
907}
908impl<'a> From<&'a tl::types::ChatEmpty> for PeerInfo {
909    fn from(chat: &'a tl::types::ChatEmpty) -> Self {
910        Self::Chat { id: chat.id }
911    }
912}
913
914impl From<tl::types::ChatForbidden> for PeerInfo {
915    #[inline]
916    fn from(chat: tl::types::ChatForbidden) -> Self {
917        <Self as From<&tl::types::ChatForbidden>>::from(&chat)
918    }
919}
920impl<'a> From<&'a tl::types::ChatForbidden> for PeerInfo {
921    fn from(chat: &'a tl::types::ChatForbidden) -> Self {
922        Self::Chat { id: chat.id }
923    }
924}
925
926impl From<tl::types::Channel> for PeerInfo {
927    #[inline]
928    fn from(channel: tl::types::Channel) -> Self {
929        <Self as From<&tl::types::Channel>>::from(&channel)
930    }
931}
932impl<'a> From<&'a tl::types::Channel> for PeerInfo {
933    fn from(channel: &'a tl::types::Channel) -> Self {
934        Self::Channel {
935            id: channel.id,
936            auth: channel.access_hash.map(PeerAuth).filter(|_| !channel.min),
937            kind: <ChannelKind as TryFrom<&'a tl::types::Channel>>::try_from(channel).ok(),
938        }
939    }
940}
941
942impl From<tl::types::ChannelForbidden> for PeerInfo {
943    #[inline]
944    fn from(channel: tl::types::ChannelForbidden) -> Self {
945        <Self as From<&tl::types::ChannelForbidden>>::from(&channel)
946    }
947}
948impl<'a> From<&'a tl::types::ChannelForbidden> for PeerInfo {
949    fn from(channel: &'a tl::types::ChannelForbidden) -> Self {
950        Self::Channel {
951            id: channel.id,
952            auth: Some(PeerAuth(channel.access_hash)),
953            kind: <ChannelKind as TryFrom<&'a tl::types::ChannelForbidden>>::try_from(channel).ok(),
954        }
955    }
956}
957
958impl TryFrom<tl::types::Channel> for ChannelKind {
959    type Error = <ChannelKind as TryFrom<&'static tl::types::Channel>>::Error;
960
961    #[inline]
962    fn try_from(channel: tl::types::Channel) -> Result<Self, Self::Error> {
963        <ChannelKind as TryFrom<&tl::types::Channel>>::try_from(&channel)
964    }
965}
966impl<'a> TryFrom<&'a tl::types::Channel> for ChannelKind {
967    type Error = ();
968
969    fn try_from(channel: &'a tl::types::Channel) -> Result<Self, Self::Error> {
970        match channel {
971            channel if channel.gigagroup => Ok(Self::Gigagroup),
972            channel if channel.broadcast => Ok(Self::Broadcast),
973            channel if channel.megagroup => Ok(Self::Megagroup),
974            _channel => Err(()),
975        }
976    }
977}
978
979impl TryFrom<tl::types::ChannelForbidden> for ChannelKind {
980    type Error = <ChannelKind as TryFrom<&'static tl::types::ChannelForbidden>>::Error;
981
982    #[inline]
983    fn try_from(channel: tl::types::ChannelForbidden) -> Result<Self, Self::Error> {
984        <ChannelKind as TryFrom<&tl::types::ChannelForbidden>>::try_from(&channel)
985    }
986}
987impl<'a> TryFrom<&'a tl::types::ChannelForbidden> for ChannelKind {
988    type Error = ();
989
990    fn try_from(channel: &'a tl::types::ChannelForbidden) -> Result<Self, Self::Error> {
991        match channel {
992            // channel if channel.gigagroup => Ok(Self::Gigagroup),
993            channel if channel.broadcast => Ok(Self::Broadcast),
994            channel if channel.megagroup => Ok(Self::Megagroup),
995            _channel => Err(()),
996        }
997    }
998}