webserver_base/telegram/
chat_id.rs1use std::fmt::{Display, Formatter};
2
3use serde::{Serialize, Serializer};
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
13pub enum ChatId {
14 Id(i64),
16 Username(String),
18}
19
20impl ChatId {
21 #[must_use]
23 pub const fn id(id: i64) -> Self {
24 Self::Id(id)
25 }
26
27 #[must_use]
29 pub fn username(username: impl AsRef<str>) -> Self {
30 let username: &str = username.as_ref();
31
32 if username.starts_with('@') {
33 Self::Username(username.to_string())
34 } else {
35 Self::Username(format!("@{username}"))
36 }
37 }
38}
39
40impl Display for ChatId {
41 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
42 match self {
43 Self::Id(id) => write!(f, "{id}"),
44 Self::Username(username) => write!(f, "{username}"),
45 }
46 }
47}
48
49impl Serialize for ChatId {
50 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
51 serializer.serialize_str(&self.to_string())
52 }
53}
54
55impl From<i64> for ChatId {
56 fn from(id: i64) -> Self {
57 Self::Id(id)
58 }
59}
60
61impl From<&str> for ChatId {
62 fn from(value: &str) -> Self {
64 let value: &str = value.trim();
65
66 value
67 .parse::<i64>()
68 .map_or_else(|_| Self::username(value), Self::Id)
69 }
70}
71
72impl From<String> for ChatId {
73 fn from(value: String) -> Self {
74 Self::from(value.as_str())
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::ChatId;
81
82 #[test]
83 fn numeric_id_round_trips() {
84 let expected: ChatId = ChatId::Id(7_518_714_136);
85 let actual: ChatId = ChatId::from(7_518_714_136_i64);
86 assert_eq!(expected, actual);
87 }
88
89 #[test]
90 fn negative_group_id_is_preserved() {
91 let expected: ChatId = ChatId::Id(-1_001_234_567_890);
92 let actual: ChatId = ChatId::from(-1_001_234_567_890_i64);
93 assert_eq!(expected, actual);
94 }
95
96 #[test]
97 fn username_gains_a_leading_at_sign() {
98 let expected: ChatId = ChatId::Username(String::from("@mychannel"));
99 let actual: ChatId = ChatId::username("mychannel");
100 assert_eq!(expected, actual);
101 }
102
103 #[test]
104 fn username_keeps_an_existing_at_sign() {
105 let expected: ChatId = ChatId::Username(String::from("@mychannel"));
106 let actual: ChatId = ChatId::username("@mychannel");
107 assert_eq!(expected, actual);
108 }
109
110 #[test]
111 fn numeric_string_parses_as_an_id() {
112 let expected: ChatId = ChatId::Id(7_518_714_136);
113 let actual: ChatId = ChatId::from("7518714136");
114 assert_eq!(expected, actual);
115 }
116
117 #[test]
118 fn non_numeric_string_parses_as_a_username() {
119 let expected: ChatId = ChatId::Username(String::from("@mychannel"));
120 let actual: ChatId = ChatId::from("@mychannel");
121 assert_eq!(expected, actual);
122 }
123
124 #[test]
125 fn numeric_id_serializes_as_a_string() {
126 let expected: String = String::from("\"7518714136\"");
127 let actual: String =
128 serde_json::to_string(&ChatId::Id(7_518_714_136)).expect("chat id should serialize");
129 assert_eq!(expected, actual);
130 }
131
132 #[test]
133 fn username_serializes_as_a_string() {
134 let expected: String = String::from("\"@mychannel\"");
135 let actual: String = serde_json::to_string(&ChatId::username("mychannel"))
136 .expect("chat id should serialize");
137 assert_eq!(expected, actual);
138 }
139}