rocketmq_protocol/protocol/body/
user_info.rs1use std::fmt;
16
17use cheetah_string::CheetahString;
18use rocketmq_error::REDACTED;
19use serde::Deserialize;
20use serde::Serialize;
21
22#[derive(Serialize, Deserialize, Default, Clone)]
23#[serde(rename_all = "camelCase")]
24pub struct UserInfo {
25 pub username: Option<CheetahString>,
26 pub password: Option<CheetahString>,
27 pub user_type: Option<CheetahString>,
28 pub user_status: Option<CheetahString>,
29}
30
31impl fmt::Debug for UserInfo {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 f.debug_struct("UserInfo")
34 .field("username", &self.username)
35 .field("password", &redacted_if_present(self.password.as_ref()))
36 .field("user_type", &self.user_type)
37 .field("user_status", &self.user_status)
38 .finish()
39 }
40}
41
42impl fmt::Display for UserInfo {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 write!(
45 f,
46 "UserInfo [username={}, password={}, user_type={}, user_status={}]",
47 self.username.as_ref().unwrap_or(&CheetahString::new()),
48 redacted_if_present(self.password.as_ref()).unwrap_or_default(),
49 self.user_type.as_ref().unwrap_or(&CheetahString::new()),
50 self.user_status.as_ref().unwrap_or(&CheetahString::new())
51 )
52 }
53}
54
55fn redacted_if_present<T>(value: Option<T>) -> Option<&'static str> {
56 value.map(|_| REDACTED)
57}
58
59#[cfg(test)]
60mod tests {
61 use serde_json;
62
63 use super::*;
64
65 #[test]
66 fn serialize_user_info() {
67 let user_info = UserInfo {
68 username: Some(CheetahString::from("user")),
69 password: Some(CheetahString::from("pass")),
70 user_type: Some(CheetahString::from("admin")),
71 user_status: Some(CheetahString::from("active")),
72 };
73 let serialized = serde_json::to_string(&user_info).unwrap();
74 assert!(serialized.contains("\"username\":\"user\""));
75 assert!(serialized.contains("\"password\":\"pass\""));
76 assert!(serialized.contains("\"userType\":\"admin\""));
77 assert!(serialized.contains("\"userStatus\":\"active\""));
78 }
79
80 #[test]
81 fn deserialize_user_info() {
82 let json = r#"{
83 "username": "user",
84 "password": "pass",
85 "userType": "admin",
86 "userStatus": "active"
87 }"#;
88 let deserialized: UserInfo = serde_json::from_str(json).unwrap();
89 assert_eq!(deserialized.username, Some(CheetahString::from("user")));
90 assert_eq!(deserialized.password, Some(CheetahString::from("pass")));
91 assert_eq!(deserialized.user_type, Some(CheetahString::from("admin")));
92 assert_eq!(deserialized.user_status, Some(CheetahString::from("active")));
93 }
94
95 #[test]
96 fn deserialize_user_info_missing_optional_fields() {
97 let json = r#"{}"#;
98 let deserialized: UserInfo = serde_json::from_str(json).unwrap();
99 assert!(deserialized.username.is_none());
100 assert!(deserialized.password.is_none());
101 assert!(deserialized.user_type.is_none());
102 assert!(deserialized.user_status.is_none());
103 }
104
105 #[test]
106 fn display_user_info() {
107 let user_info = UserInfo {
108 username: Some(CheetahString::from("user")),
109 password: Some(CheetahString::from("pass")),
110 user_type: Some(CheetahString::from("admin")),
111 user_status: Some(CheetahString::from("active")),
112 };
113 let display = format!("{}", user_info);
114 assert_eq!(
115 display,
116 "UserInfo [username=user, password=<redacted>, user_type=admin, user_status=active]"
117 );
118 assert!(!display.contains("password=pass"));
119 }
120
121 #[test]
122 fn debug_user_info_redacts_password() {
123 let user_info = UserInfo {
124 username: Some(CheetahString::from("user")),
125 password: Some(CheetahString::from("plain-password")),
126 user_type: Some(CheetahString::from("admin")),
127 user_status: Some(CheetahString::from("active")),
128 };
129 let debug = format!("{user_info:?}");
130
131 assert!(debug.contains("user"));
132 assert!(debug.contains(REDACTED));
133 assert!(!debug.contains("plain-password"));
134 }
135}