use std::collections::HashMap;
use std::sync::{Mutex, OnceLock};
pub fn short_type_name<T: 'static>() -> &'static str {
static CACHE: OnceLock<Mutex<HashMap<&'static str, &'static str>>> = OnceLock::new();
let full: &'static str = std::any::type_name::<T>();
let cache = CACHE.get_or_init(|| Mutex::new(HashMap::new()));
let mut guard = cache.lock().unwrap();
if let Some(&short) = guard.get(full) {
return short;
}
let short_owned = shorten_type_name(full);
let short: &'static str = Box::leak(short_owned.into_boxed_str());
guard.insert(full, short);
short
}
fn shorten_type_name(full: &str) -> String {
let generic_start = full.find('<');
let (base, generics) = match generic_start {
Some(pos) => (&full[..pos], Some(&full[pos + 1..full.len() - 1])),
None => (full, None),
};
let short_base = base.rsplit("::").next().unwrap_or(base);
if short_base == "Option"
&& let Some(inner) = generics
{
return shorten_type_name(inner.trim());
}
match generics {
Some(params) => {
let shortened_params = split_generic_params(params)
.into_iter()
.map(|p| shorten_type_name(p.trim()))
.collect::<Vec<_>>()
.join(", ");
format!("{}<{}>", short_base, shortened_params)
}
None => short_base.to_string(),
}
}
fn split_generic_params(params: &str) -> Vec<&str> {
let mut result = Vec::new();
let mut depth = 0;
let mut start = 0;
for (i, c) in params.char_indices() {
match c {
'<' => depth += 1,
'>' => depth -= 1,
',' if depth == 0 => {
result.push(¶ms[start..i]);
start = i + 1;
}
_ => {}
}
}
result.push(¶ms[start..]);
result
}
pub trait PrintableIndex {
fn to_string() -> &'static str;
fn to_possible_strings() -> &'static [&'static str];
}
impl PrintableIndex for usize {
fn to_string() -> &'static str {
"usize"
}
fn to_possible_strings() -> &'static [&'static str] {
&["usize"]
}
}