spdlog_telegram/
recipient.rs

1use std::{borrow::Cow, convert::Infallible};
2
3use serde_json as json;
4
5#[derive(Debug, PartialEq, Eq)]
6enum TargetChatInner {
7    Id(i64),
8    Username(String),
9}
10
11// Hacky: `TargetChat` is private, but we have to set it to `pub` instead of
12// `pub(crate)` because it appears as generic in `RecipientBuilder`.
13pub(crate) mod __private {
14    use super::*;
15
16    #[derive(Debug, PartialEq, Eq)]
17    pub struct TargetChat(TargetChatInner);
18
19    impl TargetChat {
20        pub(crate) fn id(chat_id: i64) -> Self {
21            Self(TargetChatInner::Id(chat_id))
22        }
23
24        pub(crate) fn username(username: String) -> Self {
25            Self(TargetChatInner::Username(username))
26        }
27
28        pub(crate) fn into_json(self) -> json::Value {
29            match self.0 {
30                TargetChatInner::Id(id) => json::Value::Number(id.into()),
31                TargetChatInner::Username(username) => json::Value::String(username),
32            }
33        }
34    }
35}
36use __private::TargetChat;
37
38/// Represents a Telegram chat recipient.
39///
40/// Not just a chat ID or username, it can also be represented with a message
41/// thread ID or reply.
42#[derive(Debug, PartialEq, Eq)]
43pub struct Recipient {
44    pub(crate) target: TargetChat,
45    pub(crate) thread_id: Option<u64>,
46    pub(crate) reply_to: Option<(u64, Option<TargetChat>)>,
47}
48
49impl Recipient {
50    /// Gets a builder for `Recipient`.
51    pub fn builder() -> RecipientBuilder<()> {
52        RecipientBuilder {
53            target: (),
54            thread_id: None,
55            reply_to: None,
56        }
57    }
58
59    /// Constructs a `Recipient` from a chat ID.
60    ///
61    /// This is equivalent to `Recipient::builder().chat_id(chat_id).build()`.
62    pub fn chat_id(chat_id: i64) -> Self {
63        Self::builder().chat_id(chat_id).build()
64    }
65
66    /// Constructs a `Recipient` from a username.
67    ///
68    /// This is equivalent to `Recipient::builder().username(username).build()`.
69    pub fn username<S>(username: S) -> Self
70    where
71        S: Into<String>,
72    {
73        Self::builder().username(username).build()
74    }
75}
76
77impl From<i64> for Recipient {
78    fn from(chat_id: i64) -> Self {
79        Self::chat_id(chat_id)
80    }
81}
82
83macro_rules! impl_from_str_for_recipient {
84    ( $($str_ty:ty),+ ) => {
85        $(impl From<$str_ty> for Recipient {
86            fn from(username: $str_ty) -> Self {
87                Self::username(username)
88            }
89        })+
90    };
91}
92impl_from_str_for_recipient!(&str, &mut str, Box<str>, Cow<'_, str>, String, &String);
93
94pub struct RecipientBuilder<ArgC> {
95    target: ArgC,
96    thread_id: Option<u64>,
97    reply_to: Option<(u64, Option<TargetChat>)>,
98}
99
100impl<ArgC> RecipientBuilder<ArgC> {
101    pub fn chat_id(self, chat_id: i64) -> RecipientBuilder<TargetChat> {
102        RecipientBuilder {
103            target: TargetChat::id(chat_id),
104            thread_id: self.thread_id,
105            reply_to: self.reply_to,
106        }
107    }
108
109    pub fn username<S>(self, username: S) -> RecipientBuilder<TargetChat>
110    where
111        S: Into<String>,
112    {
113        RecipientBuilder {
114            target: TargetChat::username(username.into()),
115            thread_id: self.thread_id,
116            reply_to: self.reply_to,
117        }
118    }
119
120    pub fn thread_id(mut self, thread_id: u64) -> Self {
121        self.thread_id = Some(thread_id);
122        self
123    }
124
125    pub fn reply_to(mut self, message_id: u64) -> Self {
126        self.reply_to = Some((message_id, None));
127        self
128    }
129
130    // It's not a very good name, and considering there's almost no use case for it,
131    // I chose not to make it public for now.
132    #[allow(dead_code)]
133    fn reply_to_diff_chat_id(mut self, message_id: u64, chat_id: i64) -> Self {
134        self.reply_to = Some((message_id, Some(TargetChat::id(chat_id))));
135        self
136    }
137
138    // Same as above.
139    #[allow(dead_code)]
140    fn reply_to_diff_username<S>(mut self, message_id: u64, chat_username: S) -> Self
141    where
142        S: Into<String>,
143    {
144        self.reply_to = Some((message_id, Some(TargetChat::username(chat_username.into()))));
145        self
146    }
147}
148
149impl RecipientBuilder<()> {
150    #[doc(hidden)]
151    #[deprecated(note = "\n\n\
152        builder compile-time error:\n\
153        - missing required field `chat_id` or `username`\n\n\
154    ")]
155    pub fn build(self, _: Infallible) {}
156}
157
158impl RecipientBuilder<TargetChat> {
159    pub fn build(self) -> Recipient {
160        Recipient {
161            target: self.target,
162            thread_id: self.thread_id,
163            reply_to: self.reply_to,
164        }
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171
172    #[test]
173    fn from_into() {
174        fn echo(r: impl Into<Recipient>) -> Recipient {
175            r.into()
176        }
177        assert_eq!(echo(-1001234567890), Recipient::chat_id(-1001234567890));
178        assert_eq!(echo("@username"), Recipient::username("@username"));
179    }
180}