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    /// Value used for a channel of [`tl::types::Community`] or [`tl::types::CommunityForbidden`] type.
124    Community,
125}
126
127/// Sentinel value used to represent the self-user
128/// when its true `PeerId` is unknown.
129///
130/// Per <https://core.telegram.org/api/bots/ids>:
131/// > a bot API dialog ID ranges from -4000000000000 to 1099511627775
132///
133/// This value is not intended to be visible or persisted,
134/// so it can be changed as needed in the future.
135const SELF_USER_ID: PeerId = PeerId(1 << 40);
136
137/// Sentinel value used to represent empty chats.
138///
139/// Per <https://core.telegram.org/api/bots/ids>:
140/// > \[…] transformed range for bot API chat dialog IDs is -999999999999 to -1 inclusively
141/// >
142/// > \[…] transformed range for bot API channel dialog IDs is -1997852516352 to -1000000000001 inclusively
143///
144/// `chat_id` parameters are in Telegram's API use the bare identifier,
145/// so there's no empty constructor,
146/// but it can be mimicked by picking the value in the correct range hole.
147/// This value is closer to "channel with ID 0" than "chat with ID 0",
148/// but there's no distinct `-0` integer,
149/// and channels have a proper constructor for empty already.
150const EMPTY_CHAT_ID: i64 = -1000000000000;
151
152/// https://core.telegram.org/api/bots/ids#user-ids
153const USER_ID_RANGE: RangeInclusive<i64> = 1..=0xffffffffff;
154/// https://core.telegram.org/api/bots/ids#chat-ids
155const CHAT_ID_RANGE: RangeInclusive<i64> = 1..=999999999999;
156/// https://core.telegram.org/api/bots/ids#supergroup-channel-ids
157const SUPERGROUP_AND_CHANNEL_ID_RANGE: RangeInclusive<i64> = 1..=997852516352;
158/// https://core.telegram.org/api/bots/ids#monoforum-ids
159const MONOFORUM_ID_RANGE: RangeInclusive<i64> = 1002147483649..=3000000000000;
160
161impl PeerId {
162    /// Creates a peer identity for the currently-logged-in user or bot account.
163    ///
164    /// Internally, this will use a special sentinel value outside of any valid Bot API Dialog ID range.
165    pub fn self_user() -> Self {
166        SELF_USER_ID
167    }
168
169    /// Creates a peer identity for a user or bot account.
170    pub fn user(id: i64) -> Option<Self> {
171        USER_ID_RANGE.contains(&id).then_some(Self(id))
172    }
173
174    /// Creates a peer identity for a small group chat.
175    pub fn chat(id: i64) -> Option<Self> {
176        CHAT_ID_RANGE.contains(&id).then_some(Self(-id))
177    }
178
179    /// Creates a peer identity for a broadcast channel, megagroup, gigagroup or monoforum.
180    pub fn channel(id: i64) -> Option<Self> {
181        (SUPERGROUP_AND_CHANNEL_ID_RANGE.contains(&id) || MONOFORUM_ID_RANGE.contains(&id))
182            .then_some(Self(-(1000000000000 + id)))
183    }
184
185    /// Creates a peer identity from a [Bot API dialog ID](https://core.telegram.org/api/bots/ids).
186    pub fn from_bot_api_dialog_id(id: i64) -> Option<Self> {
187        if (1..=0xffffffffff).contains(&id)
188            || (-999999999999..=-1).contains(&id)
189            || (-1997852516352..=-1000000000001).contains(&id)
190            || (-4000000000000..=-2002147483649).contains(&id)
191        {
192            Some(Self(id))
193        } else {
194            None
195        }
196    }
197
198    /// Creates a peer identity for a user or bot account.
199    /// Panics if the ID is out of the valid range.
200    #[doc(hidden)]
201    pub fn user_unchecked(id: i64) -> Self {
202        debug_assert!(USER_ID_RANGE.contains(&id), "user ID out of range");
203        Self(id)
204    }
205
206    /// Creates a peer identity for a small group chat.
207    /// Panics if the ID is out of the valid range.
208    #[doc(hidden)]
209    pub fn chat_unchecked(id: i64) -> Self {
210        debug_assert!(CHAT_ID_RANGE.contains(&id), "chat ID out of range");
211        Self(-id)
212    }
213
214    /// Creates a peer identity for a broadcast channel, megagroup, gigagroup or monoforum.
215    /// Panics if the ID is out of the valid range.
216    #[doc(hidden)]
217    pub fn channel_unchecked(id: i64) -> Self {
218        debug_assert!(
219            (SUPERGROUP_AND_CHANNEL_ID_RANGE.contains(&id) || MONOFORUM_ID_RANGE.contains(&id)),
220            "channel ID out of range"
221        );
222        Self(-(1000000000000 + id))
223    }
224
225    /// Peer kind.
226    pub fn kind(self) -> PeerKind {
227        if 1 <= self.0 && self.0 <= 0xffffffffff || self.0 == SELF_USER_ID.0 {
228            PeerKind::User
229        } else if -999999999999 <= self.0 && self.0 <= -1 {
230            PeerKind::Chat
231        } else if -1997852516352 <= self.0 && self.0 <= -1000000000001
232            || (-4000000000000 <= self.0 && self.0 <= -2002147483649)
233        {
234            PeerKind::Channel
235        } else {
236            unreachable!()
237        }
238    }
239
240    /// Returns the identity using the [Bot API dialog ID](https://core.telegram.org/api/bots/ids)
241    /// format, which has the [`PeerKind`] embedded.
242    ///
243    /// Returns `None` if the peer was constructed without providing an identifier.
244    /// For now, this can only happen if `self` represents the [`Self::self_user`].
245    pub fn bot_api_dialog_id(self) -> Option<i64> {
246        (self.0 != SELF_USER_ID.0).then_some(self.0)
247    }
248
249    /// Returns the identity using the [Bot API dialog ID](https://core.telegram.org/api/bots/ids)
250    /// format, which has the [`PeerKind`] embedded.
251    #[doc(hidden)]
252    pub fn bot_api_dialog_id_unchecked(self) -> i64 {
253        debug_assert!(self.0 != SELF_USER_ID.0);
254        self.0
255    }
256
257    /// Bare peer identifier without the [`PeerKind`] tag embedded, as used by Telegram's MTProto API.
258    ///
259    /// Returns `None` if the peer was constructed without providing an identifier.
260    /// For now, this can only happen if `self` represents the [`Self::self_user`].
261    pub fn bare_id(self) -> Option<i64> {
262        if self.0 == SELF_USER_ID.0 {
263            return None;
264        }
265        Some(match self.kind() {
266            PeerKind::User => self.0,
267            PeerKind::Chat => -self.0,
268            PeerKind::Channel => -self.0 - 1000000000000,
269        })
270    }
271
272    /// Bare peer identifier without the [`PeerKind`] tag embedded, as used by Telegram's MTProto API.
273    #[doc(hidden)]
274    pub fn bare_id_unchecked(self) -> i64 {
275        debug_assert!(self.0 != SELF_USER_ID.0);
276        match self.kind() {
277            PeerKind::User => self.0,
278            PeerKind::Chat => -self.0,
279            PeerKind::Channel => -self.0 - 1000000000000,
280        }
281    }
282
283    /// Returns the `PeerRef` for this identifier with ambient authority (i.e. [`PeerAuth::default`]).
284    ///
285    /// This is useful when [`crate::Session::peer_ref`] returns `None` due to not having any cached.
286    ///
287    /// # Examples
288    ///
289    /// ```
290    /// # async fn f(session: &grammers_session::storages::ErasedSession, peer_id: grammers_session::types::PeerId) -> Result<(), Box<dyn std::error::Error>> {
291    /// let peer_ref = session
292    ///     .peer_ref(peer_id)
293    ///     .await
294    ///     .unwrap()
295    ///     .unwrap_or(peer_id.to_ambient_ref());
296    ///
297    /// // Can try using the `peer_ref` to e.g. send messages now, even if no auth was found in cache,
298    /// // if Telegram deems you have permission to do so (e.g. you're a bot or peer is your contact).
299    /// # Ok(())
300    /// # }
301    /// ```
302    pub fn to_ambient_ref(self) -> PeerRef {
303        PeerRef {
304            id: self,
305            auth: PeerAuth::default(),
306        }
307    }
308}
309
310impl PeerAuth {
311    /// Construct a new peer authentication using Telegram's `access_hash` value.
312    pub fn from_hash(access_hash: i64) -> Self {
313        PeerAuth(access_hash)
314    }
315
316    /// Grants access to the internal access hash.
317    pub fn hash(self) -> i64 {
318        self.0
319    }
320}
321
322impl Default for PeerAuth {
323    /// Returns the ambient authority to authorize peers only when Telegram considers it valid.
324    ///
325    /// The internal representation uses `0` to signal the ambient authority,
326    /// although this might happen to be the actual witness used by some peers.
327    fn default() -> Self {
328        Self(0)
329    }
330}
331
332impl PeerInfo {
333    /// Returns the `PeerId` represented by this info.
334    pub fn id(&self) -> PeerId {
335        match self {
336            PeerInfo::User { id, .. } => PeerId::user_unchecked(*id),
337            PeerInfo::Chat { id } => PeerId::chat_unchecked(*id),
338            PeerInfo::Channel { id, .. } => PeerId::channel_unchecked(*id),
339        }
340    }
341
342    /// Returns the `PeerAuth` stored in this info.
343    pub fn auth(&self) -> Option<PeerAuth> {
344        match self {
345            PeerInfo::User { auth, .. } => *auth,
346            PeerInfo::Chat { .. } => Some(PeerAuth::default()),
347            PeerInfo::Channel { auth, .. } => *auth,
348        }
349    }
350
351    /// Updates self with any new information contained in the other info.
352    ///
353    /// This is equivalent to performing an "or-assign" operation on all the fields.
354    ///
355    /// Returns `true` if the other info matches in type and identifier.
356    /// If they do not match, no fields will be updated, and `false` is returned instead.
357    pub fn extend_info(&mut self, other: &PeerInfo) -> bool {
358        match (self, &other) {
359            (
360                PeerInfo::User {
361                    id,
362                    auth,
363                    bot,
364                    is_self,
365                },
366                PeerInfo::User {
367                    id: new_id,
368                    auth: new_auth,
369                    bot: new_bot,
370                    is_self: new_self,
371                },
372            ) if id == new_id => {
373                *auth = new_auth.or(*auth);
374                *bot = new_bot.or(*bot);
375                *is_self = new_self.or(*is_self);
376                true
377            }
378            (PeerInfo::Chat { id }, PeerInfo::Chat { id: new_id }) if id == new_id => true,
379            (
380                PeerInfo::Channel { id, auth, kind },
381                PeerInfo::Channel {
382                    id: new_id,
383                    auth: new_auth,
384                    kind: new_kind,
385                },
386            ) if id == new_id => {
387                *auth = new_auth.or(*auth);
388                *kind = new_kind.or(*kind);
389                true
390            }
391            _ => false,
392        }
393    }
394}
395
396impl fmt::Display for PeerId {
397    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
398        match self.bot_api_dialog_id() {
399            Some(id) => id.fmt(f),
400            None => f.write_str("<self-user>"),
401        }
402    }
403}
404
405impl From<tl::enums::Peer> for PeerId {
406    #[inline]
407    fn from(peer: tl::enums::Peer) -> Self {
408        <Self as From<&tl::enums::Peer>>::from(&peer)
409    }
410}
411impl<'a> From<&'a tl::enums::Peer> for PeerId {
412    fn from(peer: &'a tl::enums::Peer) -> Self {
413        use tl::enums::Peer;
414        match peer {
415            Peer::User(user) => Self::from(user),
416            Peer::Chat(chat) => Self::from(chat),
417            Peer::Channel(channel) => Self::from(channel),
418        }
419    }
420}
421
422impl From<tl::types::PeerUser> for PeerId {
423    #[inline]
424    fn from(user: tl::types::PeerUser) -> Self {
425        <Self as From<&tl::types::PeerUser>>::from(&user)
426    }
427}
428impl<'a> From<&'a tl::types::PeerUser> for PeerId {
429    fn from(user: &'a tl::types::PeerUser) -> Self {
430        Self::user_unchecked(user.user_id)
431    }
432}
433
434impl From<tl::types::PeerChat> for PeerId {
435    #[inline]
436    fn from(user: tl::types::PeerChat) -> Self {
437        <Self as From<&tl::types::PeerChat>>::from(&user)
438    }
439}
440impl<'a> From<&'a tl::types::PeerChat> for PeerId {
441    fn from(chat: &'a tl::types::PeerChat) -> Self {
442        Self::chat_unchecked(chat.chat_id)
443    }
444}
445
446impl From<tl::types::PeerChannel> for PeerId {
447    #[inline]
448    fn from(channel: tl::types::PeerChannel) -> Self {
449        <Self as From<&tl::types::PeerChannel>>::from(&channel)
450    }
451}
452impl<'a> From<&'a tl::types::PeerChannel> for PeerId {
453    fn from(channel: &'a tl::types::PeerChannel) -> Self {
454        Self::channel_unchecked(channel.channel_id)
455    }
456}
457
458impl From<tl::enums::InputPeer> for PeerRef {
459    #[inline]
460    fn from(peer: tl::enums::InputPeer) -> Self {
461        <Self as From<&tl::enums::InputPeer>>::from(&peer)
462    }
463}
464impl<'a> From<&'a tl::enums::InputPeer> for PeerRef {
465    fn from(peer: &'a tl::enums::InputPeer) -> Self {
466        use tl::{enums::InputPeer, types::InputPeerSelf};
467        match peer {
468            InputPeer::Empty => panic!("InputPeer::Empty cannot be converted to any Peer"),
469            InputPeer::PeerSelf => <Self as From<&'a _>>::from(&InputPeerSelf {}),
470            InputPeer::User(user) => <Self as From<&'a _>>::from(user),
471            InputPeer::Chat(chat) => <Self as From<&'a _>>::from(chat),
472            InputPeer::Channel(channel) => <Self as From<&'a _>>::from(channel),
473            InputPeer::UserFromMessage(user) => <Self as From<&'a _>>::from(user.deref()),
474            InputPeer::ChannelFromMessage(channel) => <Self as From<&'a _>>::from(channel.deref()),
475        }
476    }
477}
478
479impl From<tl::types::InputPeerSelf> for PeerRef {
480    #[inline]
481    fn from(self_user: tl::types::InputPeerSelf) -> Self {
482        <Self as From<&tl::types::InputPeerSelf>>::from(&self_user)
483    }
484}
485impl<'a> From<&'a tl::types::InputPeerSelf> for PeerRef {
486    fn from(self_user: &'a tl::types::InputPeerSelf) -> Self {
487        _ = self_user;
488        Self {
489            id: SELF_USER_ID,
490            auth: PeerAuth::default(),
491        }
492    }
493}
494
495impl From<tl::types::InputPeerUser> for PeerRef {
496    #[inline]
497    fn from(user: tl::types::InputPeerUser) -> Self {
498        <Self as From<&tl::types::InputPeerUser>>::from(&user)
499    }
500}
501impl<'a> From<&'a tl::types::InputPeerUser> for PeerRef {
502    fn from(user: &'a tl::types::InputPeerUser) -> Self {
503        Self {
504            id: PeerId::user_unchecked(user.user_id),
505            auth: PeerAuth::from_hash(user.access_hash),
506        }
507    }
508}
509
510impl From<tl::types::InputPeerChat> for PeerRef {
511    #[inline]
512    fn from(chat: tl::types::InputPeerChat) -> Self {
513        <Self as From<&tl::types::InputPeerChat>>::from(&chat)
514    }
515}
516impl<'a> From<&'a tl::types::InputPeerChat> for PeerRef {
517    fn from(chat: &'a tl::types::InputPeerChat) -> Self {
518        Self {
519            id: PeerId::chat_unchecked(chat.chat_id),
520            auth: PeerAuth::default(),
521        }
522    }
523}
524
525impl From<tl::types::InputPeerChannel> for PeerRef {
526    #[inline]
527    fn from(channel: tl::types::InputPeerChannel) -> Self {
528        <Self as From<&tl::types::InputPeerChannel>>::from(&channel)
529    }
530}
531impl<'a> From<&'a tl::types::InputPeerChannel> for PeerRef {
532    fn from(channel: &'a tl::types::InputPeerChannel) -> Self {
533        Self {
534            id: PeerId::channel_unchecked(channel.channel_id),
535            auth: PeerAuth::from_hash(channel.access_hash),
536        }
537    }
538}
539
540impl From<tl::types::InputPeerUserFromMessage> for PeerRef {
541    #[inline]
542    fn from(user: tl::types::InputPeerUserFromMessage) -> Self {
543        <Self as From<&tl::types::InputPeerUserFromMessage>>::from(&user)
544    }
545}
546impl<'a> From<&'a tl::types::InputPeerUserFromMessage> for PeerRef {
547    fn from(user: &'a tl::types::InputPeerUserFromMessage) -> Self {
548        // Not currently willing to make PeerRef significantly larger to accomodate for this uncommon type.
549        Self {
550            id: PeerId::user_unchecked(user.user_id),
551            auth: PeerAuth::default(),
552        }
553    }
554}
555
556impl From<tl::types::InputPeerChannelFromMessage> for PeerRef {
557    #[inline]
558    fn from(channel: tl::types::InputPeerChannelFromMessage) -> Self {
559        <Self as From<&tl::types::InputPeerChannelFromMessage>>::from(&channel)
560    }
561}
562impl<'a> From<&'a tl::types::InputPeerChannelFromMessage> for PeerRef {
563    fn from(channel: &'a tl::types::InputPeerChannelFromMessage) -> Self {
564        // Not currently willing to make PeerRef significantly larger to accomodate for this uncommon type.
565        Self {
566            id: PeerId::channel_unchecked(channel.channel_id),
567            auth: PeerAuth::default(),
568        }
569    }
570}
571
572impl From<tl::enums::User> for PeerRef {
573    #[inline]
574    fn from(user: tl::enums::User) -> Self {
575        <Self as From<&tl::enums::User>>::from(&user)
576    }
577}
578impl<'a> From<&'a tl::enums::User> for PeerRef {
579    fn from(user: &'a tl::enums::User) -> Self {
580        use tl::enums::User;
581        match user {
582            User::Empty(user) => <Self as From<&_>>::from(user),
583            User::User(user) => <Self as From<&_>>::from(user),
584        }
585    }
586}
587
588impl From<tl::types::UserEmpty> for PeerRef {
589    #[inline]
590    fn from(user: tl::types::UserEmpty) -> Self {
591        <Self as From<&tl::types::UserEmpty>>::from(&user)
592    }
593}
594impl<'a> From<&'a tl::types::UserEmpty> for PeerRef {
595    fn from(user: &'a tl::types::UserEmpty) -> Self {
596        Self {
597            id: PeerId::user_unchecked(user.id),
598            auth: PeerAuth::default(),
599        }
600    }
601}
602
603impl From<tl::types::User> for PeerRef {
604    #[inline]
605    fn from(user: tl::types::User) -> Self {
606        <Self as From<&tl::types::User>>::from(&user)
607    }
608}
609impl<'a> From<&'a tl::types::User> for PeerRef {
610    fn from(user: &'a tl::types::User) -> Self {
611        Self {
612            id: PeerId::user_unchecked(user.id),
613            auth: user
614                .access_hash
615                .map(PeerAuth::from_hash)
616                .unwrap_or_default(),
617        }
618    }
619}
620
621impl From<tl::enums::Chat> for PeerRef {
622    #[inline]
623    fn from(chat: tl::enums::Chat) -> Self {
624        <Self as From<&tl::enums::Chat>>::from(&chat)
625    }
626}
627impl<'a> From<&'a tl::enums::Chat> for PeerRef {
628    fn from(chat: &'a tl::enums::Chat) -> Self {
629        use tl::enums::Chat;
630        match chat {
631            Chat::Empty(chat) => <Self as From<&_>>::from(chat),
632            Chat::Chat(chat) => <Self as From<&_>>::from(chat),
633            Chat::Forbidden(chat) => <Self as From<&_>>::from(chat),
634            Chat::Channel(channel) => <Self as From<&_>>::from(channel),
635            Chat::ChannelForbidden(channel) => <Self as From<&_>>::from(channel),
636            Chat::CommunityForbidden(community) => <Self as From<&_>>::from(community),
637            Chat::Community(community) => <Self as From<&_>>::from(community),
638        }
639    }
640}
641
642impl From<tl::types::ChatEmpty> for PeerRef {
643    #[inline]
644    fn from(chat: tl::types::ChatEmpty) -> Self {
645        <Self as From<&tl::types::ChatEmpty>>::from(&chat)
646    }
647}
648impl<'a> From<&'a tl::types::ChatEmpty> for PeerRef {
649    fn from(chat: &'a tl::types::ChatEmpty) -> Self {
650        Self {
651            id: PeerId::chat_unchecked(chat.id),
652            auth: PeerAuth::default(),
653        }
654    }
655}
656
657impl From<tl::types::Chat> for PeerRef {
658    #[inline]
659    fn from(chat: tl::types::Chat) -> Self {
660        <Self as From<&tl::types::Chat>>::from(&chat)
661    }
662}
663impl<'a> From<&'a tl::types::Chat> for PeerRef {
664    fn from(chat: &'a tl::types::Chat) -> Self {
665        Self {
666            id: PeerId::chat_unchecked(chat.id),
667            auth: PeerAuth::default(),
668        }
669    }
670}
671
672impl From<tl::types::ChatForbidden> for PeerRef {
673    #[inline]
674    fn from(chat: tl::types::ChatForbidden) -> Self {
675        <Self as From<&tl::types::ChatForbidden>>::from(&chat)
676    }
677}
678impl<'a> From<&'a tl::types::ChatForbidden> for PeerRef {
679    fn from(chat: &'a tl::types::ChatForbidden) -> Self {
680        Self {
681            id: PeerId::chat_unchecked(chat.id),
682            auth: PeerAuth::default(),
683        }
684    }
685}
686
687impl From<tl::types::Channel> for PeerRef {
688    #[inline]
689    fn from(channel: tl::types::Channel) -> Self {
690        <Self as From<&tl::types::Channel>>::from(&channel)
691    }
692}
693impl<'a> From<&'a tl::types::Channel> for PeerRef {
694    fn from(channel: &'a tl::types::Channel) -> Self {
695        Self {
696            id: PeerId::channel_unchecked(channel.id),
697            auth: channel
698                .access_hash
699                .map(PeerAuth::from_hash)
700                .unwrap_or_default(),
701        }
702    }
703}
704
705impl From<tl::types::ChannelForbidden> for PeerRef {
706    #[inline]
707    fn from(channel: tl::types::ChannelForbidden) -> Self {
708        <Self as From<&tl::types::ChannelForbidden>>::from(&channel)
709    }
710}
711impl<'a> From<&'a tl::types::ChannelForbidden> for PeerRef {
712    fn from(channel: &'a tl::types::ChannelForbidden) -> Self {
713        Self {
714            id: PeerId::channel_unchecked(channel.id),
715            auth: PeerAuth::from_hash(channel.access_hash),
716        }
717    }
718}
719
720impl From<tl::types::CommunityForbidden> for PeerRef {
721    #[inline]
722    fn from(community: tl::types::CommunityForbidden) -> Self {
723        <Self as From<&tl::types::CommunityForbidden>>::from(&community)
724    }
725}
726impl<'a> From<&'a tl::types::CommunityForbidden> for PeerRef {
727    fn from(community: &'a tl::types::CommunityForbidden) -> Self {
728        Self {
729            id: PeerId::channel_unchecked(community.id),
730            auth: community
731                .access_hash
732                .map(PeerAuth::from_hash)
733                .unwrap_or_default(),
734        }
735    }
736}
737
738impl From<tl::types::Community> for PeerRef {
739    #[inline]
740    fn from(community: tl::types::Community) -> Self {
741        <Self as From<&tl::types::Community>>::from(&community)
742    }
743}
744impl<'a> From<&'a tl::types::Community> for PeerRef {
745    fn from(community: &'a tl::types::Community) -> Self {
746        Self {
747            id: PeerId::channel_unchecked(community.id),
748            auth: community
749                .access_hash
750                .map(PeerAuth::from_hash)
751                .unwrap_or_default(),
752        }
753    }
754}
755
756impl From<PeerId> for tl::enums::Peer {
757    #[inline]
758    fn from(peer: PeerId) -> Self {
759        <Self as From<&PeerId>>::from(&peer)
760    }
761}
762impl<'a> From<&'a PeerId> for tl::enums::Peer {
763    fn from(peer: &'a PeerId) -> Self {
764        match peer.kind() {
765            PeerKind::User => Self::User(tl::types::PeerUser {
766                user_id: peer.bare_id_unchecked(), // may have bogus data on release builds
767            }),
768            PeerKind::Chat => Self::Chat(tl::types::PeerChat {
769                chat_id: peer.bare_id_unchecked(),
770            }),
771            PeerKind::Channel => Self::Channel(tl::types::PeerChannel {
772                channel_id: peer.bare_id_unchecked(),
773            }),
774        }
775    }
776}
777
778/// Generate owned `From<PeerRef>` impls that forward to the `From<&PeerRef>` impl.
779macro_rules! forward_peer_ref_from {
780    ($($ty:ty),+ $(,)?) => {$(
781        impl From<PeerRef> for $ty {
782            #[inline]
783            fn from(peer: PeerRef) -> Self {
784                <Self as From<&PeerRef>>::from(&peer)
785            }
786        }
787    )+};
788}
789
790forward_peer_ref_from!(
791    tl::enums::InputPeer,
792    tl::enums::InputUser,
793    i64,
794    tl::enums::InputChannel,
795);
796
797impl<'a> From<&'a PeerRef> for tl::enums::InputPeer {
798    fn from(peer: &'a PeerRef) -> Self {
799        match peer.id.kind() {
800            PeerKind::User => match peer.id.bare_id() {
801                Some(user_id) => Self::User(tl::types::InputPeerUser {
802                    user_id,
803                    access_hash: peer.auth.hash(),
804                }),
805                None => Self::PeerSelf,
806            },
807            PeerKind::Chat => Self::Chat(tl::types::InputPeerChat {
808                chat_id: peer.id.bare_id_unchecked(),
809            }),
810            PeerKind::Channel => Self::Channel(tl::types::InputPeerChannel {
811                channel_id: peer.id.bare_id_unchecked(),
812                access_hash: peer.auth.hash(),
813            }),
814        }
815    }
816}
817
818impl<'a> From<&'a PeerRef> for tl::enums::InputUser {
819    fn from(peer: &'a PeerRef) -> Self {
820        match peer.id.kind() {
821            PeerKind::User => match peer.id.bare_id() {
822                Some(user_id) => Self::User(tl::types::InputUser {
823                    user_id,
824                    access_hash: peer.auth.hash(),
825                }),
826                None => Self::UserSelf,
827            },
828            PeerKind::Chat => Self::Empty,
829            PeerKind::Channel => Self::Empty,
830        }
831    }
832}
833
834impl<'a> From<&'a PeerRef> for i64 {
835    fn from(peer: &'a PeerRef) -> Self {
836        match peer.id.kind() {
837            PeerKind::User => EMPTY_CHAT_ID,
838            PeerKind::Chat => peer.id.bare_id_unchecked(),
839            PeerKind::Channel => EMPTY_CHAT_ID,
840        }
841    }
842}
843
844impl<'a> From<&'a PeerRef> for tl::enums::InputChannel {
845    fn from(peer: &'a PeerRef) -> Self {
846        match peer.id.kind() {
847            PeerKind::User => Self::Empty,
848            PeerKind::Chat => Self::Empty,
849            PeerKind::Channel => Self::Channel(tl::types::InputChannel {
850                channel_id: peer.id.bare_id_unchecked(),
851                access_hash: peer.auth.hash(),
852            }),
853        }
854    }
855}
856
857impl From<tl::enums::Chat> for PeerInfo {
858    #[inline]
859    fn from(chat: tl::enums::Chat) -> Self {
860        <Self as From<&tl::enums::Chat>>::from(&chat)
861    }
862}
863impl<'a> From<&'a tl::enums::Chat> for PeerInfo {
864    fn from(chat: &'a tl::enums::Chat) -> Self {
865        match chat {
866            tl::enums::Chat::Chat(chat) => <Self as From<&tl::types::Chat>>::from(chat),
867            tl::enums::Chat::Empty(chat) => <Self as From<&tl::types::ChatEmpty>>::from(chat),
868            tl::enums::Chat::Forbidden(chat) => {
869                <Self as From<&tl::types::ChatForbidden>>::from(chat)
870            }
871            tl::enums::Chat::Channel(channel) => <Self as From<&tl::types::Channel>>::from(channel),
872            tl::enums::Chat::ChannelForbidden(channel) => {
873                <Self as From<&tl::types::ChannelForbidden>>::from(channel)
874            }
875            tl::enums::Chat::CommunityForbidden(community) => {
876                <Self as From<&tl::types::CommunityForbidden>>::from(community)
877            }
878            tl::enums::Chat::Community(community) => {
879                <Self as From<&tl::types::Community>>::from(community)
880            }
881        }
882    }
883}
884
885impl From<tl::enums::User> for PeerInfo {
886    #[inline]
887    fn from(user: tl::enums::User) -> Self {
888        <Self as From<&tl::enums::User>>::from(&user)
889    }
890}
891impl<'a> From<&'a tl::enums::User> for PeerInfo {
892    fn from(user: &'a tl::enums::User) -> Self {
893        match user {
894            tl::enums::User::User(user) => <Self as From<&tl::types::User>>::from(user),
895            tl::enums::User::Empty(user) => <Self as From<&tl::types::UserEmpty>>::from(user),
896        }
897    }
898}
899
900impl From<tl::types::User> for PeerInfo {
901    #[inline]
902    fn from(user: tl::types::User) -> Self {
903        <Self as From<&tl::types::User>>::from(&user)
904    }
905}
906impl<'a> From<&'a tl::types::User> for PeerInfo {
907    fn from(user: &'a tl::types::User) -> Self {
908        Self::User {
909            id: user.id,
910            auth: user.access_hash.map(PeerAuth).filter(|_| !user.min),
911            bot: Some(user.bot),
912            is_self: Some(user.is_self),
913        }
914    }
915}
916
917impl From<tl::types::UserEmpty> for PeerInfo {
918    #[inline]
919    fn from(user: tl::types::UserEmpty) -> Self {
920        <Self as From<&tl::types::UserEmpty>>::from(&user)
921    }
922}
923impl<'a> From<&'a tl::types::UserEmpty> for PeerInfo {
924    fn from(user: &'a tl::types::UserEmpty) -> Self {
925        Self::User {
926            id: user.id,
927            auth: None,
928            bot: None,
929            is_self: None,
930        }
931    }
932}
933
934impl From<tl::types::Chat> for PeerInfo {
935    #[inline]
936    fn from(chat: tl::types::Chat) -> Self {
937        <Self as From<&tl::types::Chat>>::from(&chat)
938    }
939}
940impl<'a> From<&'a tl::types::Chat> for PeerInfo {
941    fn from(chat: &'a tl::types::Chat) -> Self {
942        Self::Chat { id: chat.id }
943    }
944}
945
946impl From<tl::types::ChatEmpty> for PeerInfo {
947    #[inline]
948    fn from(chat: tl::types::ChatEmpty) -> Self {
949        <Self as From<&tl::types::ChatEmpty>>::from(&chat)
950    }
951}
952impl<'a> From<&'a tl::types::ChatEmpty> for PeerInfo {
953    fn from(chat: &'a tl::types::ChatEmpty) -> Self {
954        Self::Chat { id: chat.id }
955    }
956}
957
958impl From<tl::types::ChatForbidden> for PeerInfo {
959    #[inline]
960    fn from(chat: tl::types::ChatForbidden) -> Self {
961        <Self as From<&tl::types::ChatForbidden>>::from(&chat)
962    }
963}
964impl<'a> From<&'a tl::types::ChatForbidden> for PeerInfo {
965    fn from(chat: &'a tl::types::ChatForbidden) -> Self {
966        Self::Chat { id: chat.id }
967    }
968}
969
970impl From<tl::types::Channel> for PeerInfo {
971    #[inline]
972    fn from(channel: tl::types::Channel) -> Self {
973        <Self as From<&tl::types::Channel>>::from(&channel)
974    }
975}
976impl<'a> From<&'a tl::types::Channel> for PeerInfo {
977    fn from(channel: &'a tl::types::Channel) -> Self {
978        Self::Channel {
979            id: channel.id,
980            auth: channel.access_hash.map(PeerAuth).filter(|_| !channel.min),
981            kind: <ChannelKind as TryFrom<&'a tl::types::Channel>>::try_from(channel).ok(),
982        }
983    }
984}
985
986impl From<tl::types::ChannelForbidden> for PeerInfo {
987    #[inline]
988    fn from(channel: tl::types::ChannelForbidden) -> Self {
989        <Self as From<&tl::types::ChannelForbidden>>::from(&channel)
990    }
991}
992impl<'a> From<&'a tl::types::ChannelForbidden> for PeerInfo {
993    fn from(channel: &'a tl::types::ChannelForbidden) -> Self {
994        Self::Channel {
995            id: channel.id,
996            auth: Some(PeerAuth(channel.access_hash)),
997            kind: <ChannelKind as TryFrom<&'a tl::types::ChannelForbidden>>::try_from(channel).ok(),
998        }
999    }
1000}
1001
1002impl From<tl::types::CommunityForbidden> for PeerInfo {
1003    #[inline]
1004    fn from(community: tl::types::CommunityForbidden) -> Self {
1005        <Self as From<&tl::types::CommunityForbidden>>::from(&community)
1006    }
1007}
1008impl<'a> From<&'a tl::types::CommunityForbidden> for PeerInfo {
1009    fn from(community: &'a tl::types::CommunityForbidden) -> Self {
1010        Self::Channel {
1011            id: community.id,
1012            auth: community.access_hash.map(PeerAuth::from_hash),
1013            kind: Some(ChannelKind::Community),
1014        }
1015    }
1016}
1017
1018impl From<tl::types::Community> for PeerInfo {
1019    #[inline]
1020    fn from(community: tl::types::Community) -> Self {
1021        <Self as From<&tl::types::Community>>::from(&community)
1022    }
1023}
1024impl<'a> From<&'a tl::types::Community> for PeerInfo {
1025    fn from(community: &'a tl::types::Community) -> Self {
1026        Self::Channel {
1027            id: community.id,
1028            auth: community
1029                .access_hash
1030                .map(PeerAuth)
1031                .filter(|_| !community.min),
1032            kind: Some(ChannelKind::Community),
1033        }
1034    }
1035}
1036
1037impl TryFrom<tl::types::Channel> for ChannelKind {
1038    type Error = <ChannelKind as TryFrom<&'static tl::types::Channel>>::Error;
1039
1040    #[inline]
1041    fn try_from(channel: tl::types::Channel) -> Result<Self, Self::Error> {
1042        <ChannelKind as TryFrom<&tl::types::Channel>>::try_from(&channel)
1043    }
1044}
1045impl<'a> TryFrom<&'a tl::types::Channel> for ChannelKind {
1046    type Error = ();
1047
1048    fn try_from(channel: &'a tl::types::Channel) -> Result<Self, Self::Error> {
1049        match channel {
1050            channel if channel.gigagroup => Ok(Self::Gigagroup),
1051            channel if channel.broadcast => Ok(Self::Broadcast),
1052            channel if channel.megagroup => Ok(Self::Megagroup),
1053            _channel => Err(()),
1054        }
1055    }
1056}
1057
1058impl TryFrom<tl::types::ChannelForbidden> for ChannelKind {
1059    type Error = <ChannelKind as TryFrom<&'static tl::types::ChannelForbidden>>::Error;
1060
1061    #[inline]
1062    fn try_from(channel: tl::types::ChannelForbidden) -> Result<Self, Self::Error> {
1063        <ChannelKind as TryFrom<&tl::types::ChannelForbidden>>::try_from(&channel)
1064    }
1065}
1066impl<'a> TryFrom<&'a tl::types::ChannelForbidden> for ChannelKind {
1067    type Error = ();
1068
1069    fn try_from(channel: &'a tl::types::ChannelForbidden) -> Result<Self, Self::Error> {
1070        match channel {
1071            // channel if channel.gigagroup => Ok(Self::Gigagroup),
1072            channel if channel.broadcast => Ok(Self::Broadcast),
1073            channel if channel.megagroup => Ok(Self::Megagroup),
1074            _channel => Err(()),
1075        }
1076    }
1077}