Skip to content
Clone
d183d11
62 lines 1.5 KB Rust
use std::any::type_name;
pub fn topic_for_type<T>(prefix: &str) -> String {
let raw = type_name::<T>();
format!("{prefix}.{}", sanitize_topic_component(raw))
}
pub fn sanitize_topic_component(raw: &str) -> String {
let mut out = String::with_capacity(raw.len());
let mut last_was_underscore = false;
for ch in raw.chars() {
let allowed = ch.is_ascii_alphanumeric() || ch == '.' || ch == '-';
if allowed {
out.push(ch);
last_was_underscore = false;
} else if !last_was_underscore {
out.push('_');
last_was_underscore = true;
}
}
let trimmed = out.trim_matches('_').to_string();
if trimmed.is_empty() {
"unknown".to_string()
} else {
trimmed
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strips_module_path() {
let t = topic_for_type::<crate_test_marker::Inner>("svc");
assert!(t.starts_with("svc."));
assert!(!t.contains("::"));
assert!(!t.contains('<'));
}
#[test]
fn replaces_special_chars() {
let s = sanitize_topic_component("a::b<c>d");
assert_eq!(s, "a_b_c_d");
}
#[test]
fn collapses_replacements() {
let s = sanitize_topic_component("a:::b");
assert_eq!(s, "a_b");
}
#[test]
fn empty_fallback() {
let s = sanitize_topic_component("<<<>>>");
assert_eq!(s, "unknown");
}
mod crate_test_marker {
pub struct Inner;
}
}