teloxide_core/types/
user.rs1use serde::{Deserialize, Serialize};
2
3use crate::types::UserId;
4
5#[serde_with::skip_serializing_none]
9#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
10pub struct User {
11 pub id: UserId,
13
14 pub is_bot: bool,
16
17 pub first_name: String,
19
20 pub last_name: Option<String>,
22
23 pub username: Option<String>,
25
26 pub language_code: Option<String>,
30
31 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
33 pub is_premium: bool,
34
35 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
37 pub added_to_attachment_menu: bool,
38}
39
40impl User {
41 #[must_use]
44 pub fn full_name(&self) -> String {
45 match &self.last_name {
46 Some(last_name) => format!("{0} {1}", self.first_name, last_name),
47 None => self.first_name.clone(),
48 }
49 }
50
51 #[must_use]
54 pub fn mention(&self) -> Option<String> {
55 Some(format!("@{}", self.username.as_ref()?))
56 }
57
58 #[must_use]
61 pub fn url(&self) -> reqwest::Url {
62 self.id.url()
63 }
64
65 #[must_use]
68 pub fn tme_url(&self) -> Option<reqwest::Url> {
69 Some(format!("https://t.me/{}", self.username.as_ref()?).parse().unwrap())
70 }
71
72 #[must_use]
75 pub fn preferably_tme_url(&self) -> reqwest::Url {
76 self.tme_url().unwrap_or_else(|| self.url())
77 }
78
79 #[must_use]
82 pub fn is_anonymous(&self) -> bool {
83 debug_assert!(
85 !self.id.is_anonymous()
86 || (self.is_bot
87 && self.first_name == "Group"
88 && self.last_name.is_none()
89 && self.username.as_deref() == Some("GroupAnonymousBot"))
90 );
91
92 self.id.is_anonymous()
93 }
94
95 #[must_use]
98 pub fn is_channel(&self) -> bool {
99 debug_assert!(
101 !self.id.is_channel()
102 || (self.is_bot
103 && self.first_name == "Channel"
104 && self.last_name.is_none()
105 && self.username.as_deref() == Some("Channel_Bot"))
106 );
107
108 self.id.is_channel()
109 }
110
111 #[must_use]
117 pub fn is_telegram(&self) -> bool {
118 debug_assert!(
120 !self.id.is_telegram()
121 || (!self.is_bot
122 && self.first_name == "Telegram"
123 && self.last_name.is_none()
124 && self.username.is_none())
125 );
126
127 self.id.is_telegram()
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn deserialize() {
137 let json = r#"{
138 "id":12345,
139 "is_bot":false,
140 "first_name":"firstName",
141 "last_name":"lastName",
142 "username":"Username",
143 "language_code":"ru"
144 }"#;
145 let expected = User {
146 id: UserId(12345),
147 is_bot: false,
148 first_name: "firstName".to_string(),
149 last_name: Some("lastName".to_string()),
150 username: Some("Username".to_string()),
151 language_code: Some(String::from("ru")),
152 is_premium: false,
153 added_to_attachment_menu: false,
154 };
155 let actual = serde_json::from_str::<User>(json).unwrap();
156 assert_eq!(actual, expected)
157 }
158
159 #[test]
160 fn convenience_methods_work() {
161 let user_a = User {
162 id: UserId(43),
163 is_bot: false,
164 first_name: "First".to_owned(),
165 last_name: Some("Last".to_owned()),
166 username: Some("aaaaaaaaaaaaaaaa".to_owned()),
167 language_code: None,
168 is_premium: false,
169 added_to_attachment_menu: false,
170 };
171
172 let user_b = User {
173 id: UserId(44),
174 is_bot: false,
175 first_name: ".".to_owned(),
176 last_name: None,
177 username: None,
178 language_code: None,
179 is_premium: false,
180 added_to_attachment_menu: false,
181 };
182
183 assert_eq!(user_a.full_name(), "First Last");
184 assert_eq!(user_b.full_name(), ".");
185
186 assert_eq!(user_a.mention(), Some("@aaaaaaaaaaaaaaaa".to_owned()));
187 assert_eq!(user_b.mention(), None);
188
189 assert_eq!(user_a.tme_url(), Some("https://t.me/aaaaaaaaaaaaaaaa".parse().unwrap()));
190 assert_eq!(user_b.tme_url(), None);
191
192 assert_eq!(user_a.preferably_tme_url(), "https://t.me/aaaaaaaaaaaaaaaa".parse().unwrap());
193 assert_eq!(user_b.preferably_tme_url(), "tg://user/?id=44".parse().unwrap());
194 }
195}