rocketmq_protocol/protocol/heartbeat/
subscription_data.rs1use std::collections::BTreeSet;
16use std::hash::Hash;
17use std::hash::Hasher;
18
19use cheetah_string::CheetahString;
20use rocketmq_model::common::filter::expression_type::ExpressionType;
21use serde::Deserialize;
22use serde::Serialize;
23
24#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
25#[serde(rename_all = "camelCase")]
26pub struct SubscriptionData {
27 pub class_filter_mode: bool,
28 pub topic: CheetahString,
29 pub sub_string: CheetahString,
30 pub tags_set: BTreeSet<CheetahString>,
31 pub code_set: BTreeSet<i32>,
32 pub sub_version: i64,
33 pub expression_type: CheetahString,
34 #[serde(skip)]
37 pub filter_class_source: CheetahString, }
39
40impl Default for SubscriptionData {
41 fn default() -> Self {
42 SubscriptionData {
43 class_filter_mode: false,
44 topic: CheetahString::new(),
45 sub_string: CheetahString::new(),
46 tags_set: BTreeSet::new(),
47 code_set: BTreeSet::new(),
48 sub_version: 0,
49 expression_type: CheetahString::from_static_str(ExpressionType::TAG),
50 filter_class_source: CheetahString::new(),
51 }
52 }
53}
54
55impl SubscriptionData {
56 pub const SUB_ALL: &'static str = "*";
57}
58
59impl Hash for SubscriptionData {
60 fn hash<H: Hasher>(&self, state: &mut H) {
61 self.class_filter_mode.hash(state);
62 self.topic.hash(state);
63 self.sub_string.hash(state);
64 self.tags_set.iter().for_each(|tag| tag.hash(state));
65 self.code_set.iter().for_each(|code| code.hash(state));
66 self.sub_version.hash(state);
67 self.expression_type.hash(state);
68 self.filter_class_source.hash(state);
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75 use std::collections::hash_map::DefaultHasher;
76
77 #[test]
78 fn subscription_data_default() {
79 let data = SubscriptionData::default();
80
81 assert!(!data.class_filter_mode);
82 assert!(data.topic.is_empty());
83 assert!(data.sub_string.is_empty());
84 assert!(data.tags_set.is_empty());
85 assert!(data.code_set.is_empty());
86 assert_eq!(data.sub_version, 0);
87 assert_eq!(data.expression_type, ExpressionType::TAG);
88 assert!(data.filter_class_source.is_empty());
89 }
90
91 #[test]
92 fn subscription_data_sub_all_constant() {
93 assert_eq!(SubscriptionData::SUB_ALL, "*");
94 }
95
96 #[test]
97 fn subscription_data_hash_and_eq() {
98 let data1 = SubscriptionData {
99 topic: CheetahString::from("topic"),
100 sub_version: 12345,
101 ..Default::default()
102 };
103
104 let mut data2 = data1.clone();
105 assert_eq!(data1, data2);
106
107 let mut hasher1 = DefaultHasher::new();
108 data1.hash(&mut hasher1);
109 let hash1 = hasher1.finish();
110
111 let mut hasher2 = DefaultHasher::new();
112 data2.hash(&mut hasher2);
113 let hash2 = hasher2.finish();
114
115 assert_eq!(hash1, hash2);
116
117 data2.sub_version = 54321;
118 assert_ne!(data1, data2);
119 }
120
121 #[test]
122 fn subscription_data_serde() {
123 let mut data = SubscriptionData {
124 topic: CheetahString::from("test_topic"),
125 filter_class_source: CheetahString::from("source"),
126 ..Default::default()
127 };
128 data.tags_set.insert(CheetahString::from("tag1"));
129 data.code_set.insert(1);
130
131 let json = serde_json::to_string(&data).unwrap();
132 assert!(json.contains("\"topic\":\"test_topic\""));
133 assert!(json.contains("\"tagsSet\":[\"tag1\"]"));
134 assert!(json.contains("\"codeSet\":[1]"));
135 assert!(!json.contains("filterClassSource"));
136 assert!(!json.contains("source"));
137
138 let deserialized: SubscriptionData = serde_json::from_str(&json).unwrap();
139 assert_eq!(deserialized.topic, data.topic);
140 assert_eq!(deserialized.tags_set, data.tags_set);
141 assert_eq!(deserialized.code_set, data.code_set);
142 assert_eq!(deserialized.sub_version, data.sub_version);
143 assert!(deserialized.filter_class_source.is_empty());
144 }
145}