use std::collections::BTreeSet;
use std::hash::Hash;
use std::hash::Hasher;
use cheetah_string::CheetahString;
use rocketmq_common::common::filter::expression_type::ExpressionType;
use rocketmq_common::TimeUtils::current_millis;
use serde::Deserialize;
use serde::Serialize;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "camelCase")]
pub struct SubscriptionData {
pub class_filter_mode: bool,
pub topic: CheetahString,
pub sub_string: CheetahString,
pub tags_set: BTreeSet<CheetahString>,
pub code_set: BTreeSet<i32>,
pub sub_version: i64,
pub expression_type: CheetahString,
#[serde(skip)]
pub filter_class_source: CheetahString, }
impl Default for SubscriptionData {
fn default() -> Self {
SubscriptionData {
class_filter_mode: false,
topic: CheetahString::new(),
sub_string: CheetahString::new(),
tags_set: BTreeSet::new(),
code_set: BTreeSet::new(),
sub_version: current_millis() as i64,
expression_type: CheetahString::from_static_str(ExpressionType::TAG),
filter_class_source: CheetahString::new(),
}
}
}
impl SubscriptionData {
pub const SUB_ALL: &'static str = "*";
}
impl Hash for SubscriptionData {
fn hash<H: Hasher>(&self, state: &mut H) {
self.class_filter_mode.hash(state);
self.topic.hash(state);
self.sub_string.hash(state);
self.tags_set.iter().for_each(|tag| tag.hash(state));
self.code_set.iter().for_each(|code| code.hash(state));
self.sub_version.hash(state);
self.expression_type.hash(state);
self.filter_class_source.hash(state);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::hash_map::DefaultHasher;
#[test]
fn subscription_data_default() {
let before = current_millis() as i64;
let data = SubscriptionData::default();
let after = current_millis() as i64;
assert!(!data.class_filter_mode);
assert!(data.topic.is_empty());
assert!(data.sub_string.is_empty());
assert!(data.tags_set.is_empty());
assert!(data.code_set.is_empty());
assert!(data.sub_version >= before && data.sub_version <= after);
assert_eq!(data.expression_type, ExpressionType::TAG);
assert!(data.filter_class_source.is_empty());
}
#[test]
fn subscription_data_sub_all_constant() {
assert_eq!(SubscriptionData::SUB_ALL, "*");
}
#[test]
fn subscription_data_hash_and_eq() {
let data1 = SubscriptionData {
topic: CheetahString::from("topic"),
sub_version: 12345,
..Default::default()
};
let mut data2 = data1.clone();
assert_eq!(data1, data2);
let mut hasher1 = DefaultHasher::new();
data1.hash(&mut hasher1);
let hash1 = hasher1.finish();
let mut hasher2 = DefaultHasher::new();
data2.hash(&mut hasher2);
let hash2 = hasher2.finish();
assert_eq!(hash1, hash2);
data2.sub_version = 54321;
assert_ne!(data1, data2);
}
#[test]
fn subscription_data_serde() {
let mut data = SubscriptionData {
topic: CheetahString::from("test_topic"),
filter_class_source: CheetahString::from("source"),
..Default::default()
};
data.tags_set.insert(CheetahString::from("tag1"));
data.code_set.insert(1);
let json = serde_json::to_string(&data).unwrap();
assert!(json.contains("\"topic\":\"test_topic\""));
assert!(json.contains("\"tagsSet\":[\"tag1\"]"));
assert!(json.contains("\"codeSet\":[1]"));
assert!(!json.contains("filterClassSource"));
assert!(!json.contains("source"));
let deserialized: SubscriptionData = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.topic, data.topic);
assert_eq!(deserialized.tags_set, data.tags_set);
assert_eq!(deserialized.code_set, data.code_set);
assert_eq!(deserialized.sub_version, data.sub_version);
assert!(deserialized.filter_class_source.is_empty());
}
}