//! Type-safe ID wrappers to prevent mixing up different ID types. //! //! Provides `ChatId` and `MessageId` newtypes for compile-time safety. use serde::{Deserialize, Serialize}; use std::fmt; /// Chat identifier #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ChatId(pub i64); impl ChatId { pub fn new(id: i64) -> Self { Self(id) } pub fn as_i64(&self) -> i64 { self.0 } } impl From for ChatId { fn from(id: i64) -> Self { Self(id) } } impl From for i64 { fn from(id: ChatId) -> Self { id.0 } } impl fmt::Display for ChatId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } /// Message identifier #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub struct MessageId(pub i64); impl MessageId { pub fn new(id: i64) -> Self { Self(id) } pub fn as_i64(&self) -> i64 { self.0 } } impl From for MessageId { fn from(id: i64) -> Self { Self(id) } } impl From for i64 { fn from(id: MessageId) -> Self { id.0 } } impl fmt::Display for MessageId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } /// User identifier #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct UserId(pub i64); impl UserId { pub fn new(id: i64) -> Self { Self(id) } pub fn as_i64(&self) -> i64 { self.0 } } impl From for UserId { fn from(id: i64) -> Self { Self(id) } } impl From for i64 { fn from(id: UserId) -> Self { id.0 } } impl fmt::Display for UserId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_chat_id() { let id = ChatId::new(123); assert_eq!(id.as_i64(), 123); assert_eq!(i64::from(id), 123); let id2: ChatId = 456.into(); assert_eq!(id2.0, 456); } #[test] fn test_message_id() { let id = MessageId::new(789); assert_eq!(id.as_i64(), 789); assert_eq!(i64::from(id), 789); } #[test] fn test_user_id() { let id = UserId::new(111); assert_eq!(id.as_i64(), 111); assert_eq!(i64::from(id), 111); } #[test] fn test_type_safety() { // Type safety is enforced at compile time // The following would not compile: // let chat_id = ChatId::new(1); // let message_id = MessageId::new(1); // if chat_id == message_id { } // ERROR: mismatched types // Runtime values can be the same, but types are different let chat_id = ChatId::new(1); let message_id = MessageId::new(1); assert_eq!(chat_id.as_i64(), 1); assert_eq!(message_id.as_i64(), 1); // But they cannot be compared directly due to type safety } #[test] fn test_display() { let chat_id = ChatId::new(123); assert_eq!(format!("{}", chat_id), "123"); let message_id = MessageId::new(456); assert_eq!(format!("{}", message_id), "456"); let user_id = UserId::new(789); assert_eq!(format!("{}", user_id), "789"); } #[test] fn test_hash_map() { use std::collections::HashMap; let mut map = HashMap::new(); map.insert(ChatId::new(1), "chat1"); map.insert(ChatId::new(2), "chat2"); assert_eq!(map.get(&ChatId::new(1)), Some(&"chat1")); assert_eq!(map.get(&ChatId::new(2)), Some(&"chat2")); assert_eq!(map.get(&ChatId::new(3)), None); } }