Skip to content

Commit b4ea7dc

Browse files
committed
refactor(app): refactor mqtt log formatting into format.rs module, update code usages
Signed-off-by: Deep Panchal <[email protected]>
1 parent bd1a01e commit b4ea7dc

File tree

2 files changed

+119
-87
lines changed

2 files changed

+119
-87
lines changed

src/format.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use colored::*;
2+
use serde_json::Value;
3+
use std::hash::{Hash, Hasher};
4+
5+
pub fn format_mqtt_log_entry(topic: &str, payload: &str) -> String {
6+
let color = derive_color_from_string(topic);
7+
let timestamp = chrono::Utc::now().to_rfc3339();
8+
let pretty_output = format_payload(payload);
9+
10+
let (header_text, divider) = format_header_and_divider(topic, &timestamp, color);
11+
12+
let log_section = print_log_section(&divider, &header_text);
13+
let formatted_output = format!(
14+
"{}\n{}\n{}\n",
15+
log_section,
16+
pretty_output.bright_white(),
17+
log_section,
18+
);
19+
20+
formatted_output
21+
}
22+
23+
fn format_payload(payload: &str) -> String {
24+
match serde_json::from_str::<Value>(payload) {
25+
Ok(value) => serde_json::to_string_pretty(&value).unwrap_or_else(|_| payload.to_string()),
26+
Err(_) => payload.to_string(),
27+
}
28+
}
29+
30+
fn format_header_and_divider(
31+
topic: &str,
32+
timestamp: &str,
33+
color: Color,
34+
) -> (ColoredString, ColoredString) {
35+
let terminal_width = get_terminal_width().unwrap_or(96);
36+
let timestamp_width = timestamp.len();
37+
let max_topic_width = terminal_width.saturating_sub(timestamp_width + 1);
38+
39+
let truncated_topic = truncate_topic(topic, max_topic_width);
40+
let header_text = format_header(&truncated_topic, timestamp, terminal_width, timestamp_width);
41+
let divider = "─".repeat(header_text.len());
42+
43+
let styled_header = header_text.color(color).bold();
44+
let styled_divider = divider.color(color).bold();
45+
46+
(styled_header, styled_divider)
47+
}
48+
49+
fn truncate_topic(topic: &str, max_topic_width: usize) -> String {
50+
if topic.len() > max_topic_width {
51+
format!("{}…", &topic[..max_topic_width.saturating_sub(1)])
52+
} else {
53+
topic.to_string()
54+
}
55+
}
56+
57+
fn format_header(
58+
truncated_topic: &str,
59+
timestamp: &str,
60+
terminal_width: usize,
61+
timestamp_width: usize,
62+
) -> String {
63+
if truncated_topic.len() <= terminal_width {
64+
let spacer_width = terminal_width.saturating_sub(truncated_topic.len() + timestamp_width);
65+
let spacer = " ".repeat(spacer_width);
66+
format!("{}{}{}", truncated_topic, spacer, timestamp)
67+
} else {
68+
truncated_topic.to_string()
69+
}
70+
}
71+
72+
fn get_terminal_width() -> Option<usize> {
73+
term_size::dimensions().map(|(width, _)| width)
74+
}
75+
76+
fn print_log_section(divider: &ColoredString, header: &ColoredString) -> String {
77+
format!("{}\n{}\n{}", divider, header, divider)
78+
}
79+
80+
fn derive_color_from_string(input: &str) -> Color {
81+
let mut hasher = std::collections::hash_map::DefaultHasher::new();
82+
input.hash(&mut hasher);
83+
let hash = hasher.finish();
84+
85+
// Generate vibrant colors using golden ratio distribution
86+
let hue = (hash as f64) * 0.618033988749895 % 360.0;
87+
let saturation = 75.0 + ((hash >> 8) % 25) as f64; // 75-100%
88+
let lightness = 45.0 + ((hash >> 16) % 15) as f64; // 45-60%
89+
90+
let (r, g, b) = hsl_to_rgb(hue, saturation / 100.0, lightness / 100.0);
91+
Color::TrueColor { r, g, b }
92+
}
93+
94+
fn hsl_to_rgb(h: f64, s: f64, l: f64) -> (u8, u8, u8) {
95+
let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
96+
let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
97+
let m = l - c / 2.0;
98+
99+
let (r, g, b) = match h {
100+
h if h < 60.0 => (c, x, 0.0),
101+
h if h < 120.0 => (x, c, 0.0),
102+
h if h < 180.0 => (0.0, c, x),
103+
h if h < 240.0 => (0.0, x, c),
104+
h if h < 300.0 => (x, 0.0, c),
105+
_ => (c, 0.0, x),
106+
};
107+
108+
(
109+
((r + m) * 255.0).round() as u8,
110+
((g + m) * 255.0).round() as u8,
111+
((b + m) * 255.0).round() as u8,
112+
)
113+
}

src/main.rs

Lines changed: 6 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
mod format;
2+
3+
use crate::format::format_mqtt_log_entry;
14
use aws_iot_device_sdk_rust::settings::MQTTOptionsOverrides;
25
use aws_iot_device_sdk_rust::{
36
async_event_loop_listener, AWSIoTAsyncClient, AWSIoTSettings, Packet, QoS,
@@ -8,7 +11,6 @@ use log::debug;
811
use regex::Regex;
912
use serde_json::Value;
1013
use std::error::Error;
11-
use std::hash::{Hash, Hasher};
1214
use std::sync::Arc;
1315
use tokio::signal;
1416
use tokio::sync::Mutex;
@@ -24,7 +26,7 @@ use tokio::time::{sleep, Duration};
2426
MQTT CLI for AWS IoT
2527
2628
This tool allows you to subscribe to or publish messages to AWS IoT topics.
27-
You can filter messages using regex patterns for inclusion or exclusion.
29+
You can filter messages from topics using regex patterns for inclusion or exclusion.
2830
2931
Examples:
3032
aws-iot-mqtt-cli sub --topics test/1234/health,test/2345/data
@@ -225,7 +227,8 @@ async fn main() -> Result<(), Box<dyn Error>> {
225227
continue;
226228
}
227229
}
228-
format_mqtt_log_entry(&topic, &payload);
230+
let formatted_output = format_mqtt_log_entry(&topic, &payload);
231+
println!("{}", formatted_output);
229232
}
230233
}
231234
});
@@ -295,87 +298,3 @@ async fn main() -> Result<(), Box<dyn Error>> {
295298

296299
Ok(())
297300
}
298-
299-
fn format_mqtt_log_entry(topic: &str, payload: &str) {
300-
let color = derive_color_from_string(topic);
301-
let timestamp = chrono::Utc::now().to_rfc3339();
302-
let pretty_output = match serde_json::from_str::<Value>(payload) {
303-
Ok(value) => serde_json::to_string_pretty(&value).unwrap_or_else(|_| payload.to_string()),
304-
Err(_) => payload.to_string(),
305-
};
306-
307-
let terminal_width = term_size::dimensions()
308-
.map(|(width, _)| width)
309-
.unwrap_or(96);
310-
311-
let timestamp_width = timestamp.len();
312-
let max_topic_width = terminal_width.saturating_sub(timestamp_width + 1);
313-
314-
let truncated_topic = if topic.len() > max_topic_width {
315-
format!("{}…", &topic[..max_topic_width.saturating_sub(1)])
316-
} else {
317-
topic.to_string()
318-
};
319-
320-
let header_text = if topic.len() <= max_topic_width {
321-
let spacer_width = terminal_width.saturating_sub(truncated_topic.len() + timestamp_width);
322-
let spacer = " ".repeat(spacer_width);
323-
format!("{}{}{}", truncated_topic, spacer, timestamp)
324-
} else if topic.len() <= terminal_width {
325-
truncated_topic
326-
} else {
327-
format!("{}…", &topic[..terminal_width.saturating_sub(1)])
328-
};
329-
330-
let divider = "─".repeat(header_text.len());
331-
let styled_divider = divider.color(color).bold();
332-
let styled_header = header_text.color(color).bold();
333-
let styled_json = pretty_output.bright_white();
334-
335-
print_log_section(&styled_divider, &styled_header);
336-
println!("{}", styled_json);
337-
print_log_section(&styled_divider, &styled_header);
338-
println!();
339-
}
340-
341-
/// Helper function to print header/footer sections
342-
fn print_log_section(divider: &ColoredString, header: &ColoredString) {
343-
println!("{}", divider);
344-
println!("{}", header);
345-
println!("{}", divider);
346-
}
347-
348-
fn derive_color_from_string(topic: &str) -> Color {
349-
let mut hasher = std::collections::hash_map::DefaultHasher::new();
350-
topic.hash(&mut hasher);
351-
let hash = hasher.finish();
352-
353-
// Generate vibrant colors using golden ratio distribution
354-
let hue = (hash as f64) * 0.618033988749895 % 360.0;
355-
let saturation = 75.0 + ((hash >> 8) % 25) as f64; // 75-100%
356-
let lightness = 45.0 + ((hash >> 16) % 15) as f64; // 45-60%
357-
358-
let (r, g, b) = hsl_to_rgb(hue, saturation / 100.0, lightness / 100.0);
359-
Color::TrueColor { r, g, b }
360-
}
361-
362-
fn hsl_to_rgb(h: f64, s: f64, l: f64) -> (u8, u8, u8) {
363-
let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
364-
let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
365-
let m = l - c / 2.0;
366-
367-
let (r, g, b) = match h {
368-
h if h < 60.0 => (c, x, 0.0),
369-
h if h < 120.0 => (x, c, 0.0),
370-
h if h < 180.0 => (0.0, c, x),
371-
h if h < 240.0 => (0.0, x, c),
372-
h if h < 300.0 => (x, 0.0, c),
373-
_ => (c, 0.0, x),
374-
};
375-
376-
(
377-
((r + m) * 255.0).round() as u8,
378-
((g + m) * 255.0).round() as u8,
379-
((b + m) * 255.0).round() as u8,
380-
)
381-
}

0 commit comments

Comments
 (0)