mecha10_core/
topic_utils.rs1use std::time::SystemTime;
9
10pub fn extract_category(topic: &str) -> String {
21 let parts: Vec<&str> = topic.trim_start_matches('/').split('/').collect();
22 if parts.is_empty() || parts[0].is_empty() {
23 "uncategorized".to_string()
24 } else {
25 format!("/{}", parts[0])
26 }
27}
28
29pub fn format_elapsed_time(elapsed_ms: u64) -> String {
40 if elapsed_ms < 1000 {
41 format!("{}ms ago", elapsed_ms)
42 } else if elapsed_ms < 60_000 {
43 format!("{}s ago", elapsed_ms / 1000)
44 } else if elapsed_ms < 3_600_000 {
45 format!("{}m ago", elapsed_ms / 60_000)
46 } else if elapsed_ms < 86_400_000 {
47 format!("{}h ago", elapsed_ms / 3_600_000)
48 } else {
49 format!("{}d ago", elapsed_ms / 86_400_000)
50 }
51}
52
53#[derive(Debug, Clone)]
55pub struct TopicMetadata {
56 pub topic: String,
58 pub message_count: Option<usize>,
60 pub last_update_ms: Option<u64>,
62 pub first_timestamp_ms: Option<u64>,
64 pub last_timestamp_ms: Option<u64>,
66}
67
68impl TopicMetadata {
69 pub fn new(topic: String) -> Self {
71 Self {
72 topic,
73 message_count: None,
74 last_update_ms: None,
75 first_timestamp_ms: None,
76 last_timestamp_ms: None,
77 }
78 }
79
80 pub fn with_message_count(mut self, count: usize) -> Self {
82 self.message_count = Some(count);
83 self
84 }
85
86 pub fn with_last_update(mut self, timestamp_ms: u64) -> Self {
88 self.last_update_ms = Some(timestamp_ms);
89 self
90 }
91
92 pub fn with_timestamp_range(mut self, first_ms: u64, last_ms: u64) -> Self {
94 self.first_timestamp_ms = Some(first_ms);
95 self.last_timestamp_ms = Some(last_ms);
96 self
97 }
98
99 pub fn elapsed_since_last_update(&self) -> Option<u64> {
101 self.last_update_ms.map(|last_ms| {
102 let now_ms = SystemTime::now()
103 .duration_since(SystemTime::UNIX_EPOCH)
104 .unwrap()
105 .as_millis() as u64;
106 now_ms.saturating_sub(last_ms)
107 })
108 }
109
110 pub fn calculate_rate(&self) -> Option<f64> {
112 if let (Some(first_ms), Some(last_ms), Some(count)) =
113 (self.first_timestamp_ms, self.last_timestamp_ms, self.message_count)
114 {
115 let duration_ms = last_ms.saturating_sub(first_ms);
116 if duration_ms > 1000 && count > 0 {
117 return Some((count as f64 * 1000.0) / duration_ms as f64);
118 }
119 }
120 None
121 }
122
123 pub fn last_update_str(&self) -> Option<String> {
125 self.elapsed_since_last_update().map(format_elapsed_time)
126 }
127
128 pub fn category(&self) -> String {
130 extract_category(&self.topic)
131 }
132}