pub(crate) const USELESS_SCOPE_NAME_SUFFIX: &str = "::{{closure}}::{{closure}}::f";
#[doc(hidden)]
#[inline(never)]
pub fn clean_function_name(name: &str) -> String {
let Some(name) = name.strip_suffix(USELESS_SCOPE_NAME_SUFFIX) else {
return name.to_owned();
};
shorten_rust_function_name(name)
}
pub fn shorten_rust_function_name(name: &str) -> String {
fn last_part(name: &str) -> &str {
if let Some(colon) = name.rfind("::") {
&name[colon + 2..]
} else {
name
}
}
if let Some(end_caret) = name.rfind('>') {
if let Some(trait_as) = name.rfind(" as ") {
if trait_as < end_caret {
let concrete_name = if let Some(start_caret) = name[..trait_as].rfind('<') {
&name[start_caret + 1..trait_as]
} else {
name
};
let trait_name = &name[trait_as + 4..end_caret];
let concrete_name = last_part(concrete_name);
let trait_name = last_part(trait_name);
let dubcolon_function_name = &name[end_caret + 1..];
return format!("<{concrete_name} as {trait_name}>{dubcolon_function_name}");
}
}
}
if let Some(colon) = name.rfind("::") {
if let Some(colon) = name[..colon].rfind("::") {
name[colon + 2..].to_owned()
} else {
name.to_owned()
}
} else {
name.to_owned()
}
}
#[doc(hidden)]
#[inline(never)]
pub fn short_file_name(path: &str) -> String {
if path.is_empty() {
return "".to_string();
}
let path = path.replace('\\', "/"); let components: Vec<&str> = path.split('/').collect();
if components.len() <= 2 {
return path;
}
let mut src_idx = None;
for (i, c) in components.iter().enumerate() {
if *c == "src" {
src_idx = Some(i);
}
}
if let Some(src_idx) = src_idx {
let crate_index = src_idx.saturating_sub(1);
let file_index = components.len() - 1;
if crate_index + 2 == file_index {
format!(
"{}/{}/{}",
components[crate_index],
components[crate_index + 1],
components[file_index]
)
} else if components[file_index] == "lib.rs" {
let folder_index = file_index - 1;
if crate_index + 1 == folder_index {
format!(
"{}/{}/{}",
components[crate_index], components[folder_index], components[file_index]
)
} else {
format!(
"{}/…/{}/{}",
components[crate_index], components[folder_index], components[file_index]
)
}
} else {
format!("{}/…/{}", components[crate_index], components[file_index])
}
} else {
let n = components.len();
format!("{}/{}", components[n - 2], components[n - 1])
}
}
#[doc(hidden)]
#[inline(always)]
pub fn type_name_of<T>(_: T) -> &'static str {
std::any::type_name::<T>()
}
#[test]
fn test_short_file_name() {
for (before, after) in [
("", ""),
("foo.rs", "foo.rs"),
("foo/bar.rs", "foo/bar.rs"),
("foo/bar/baz.rs", "bar/baz.rs"),
("crates/cratename/src/main.rs", "cratename/src/main.rs"),
("crates/cratename/src/module/lib.rs", "cratename/…/module/lib.rs"),
("workspace/cratename/examples/hello_world.rs", "examples/hello_world.rs"),
("/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/ops/function.rs", "core/…/function.rs"),
("/Users/emilk/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.24.1/src/runtime/runtime.rs", "tokio-1.24.1/…/runtime.rs"),
]
{
assert_eq!(short_file_name(before), after);
}
}
#[test]
fn test_clean_function_name() {
assert_eq!(clean_function_name(""), "");
assert_eq!(
clean_function_name(&format!("foo{}", USELESS_SCOPE_NAME_SUFFIX)),
"foo"
);
assert_eq!(
clean_function_name(&format!("foo::bar{}", USELESS_SCOPE_NAME_SUFFIX)),
"foo::bar"
);
assert_eq!(
clean_function_name(&format!("foo::bar::baz{}", USELESS_SCOPE_NAME_SUFFIX)),
"bar::baz"
);
assert_eq!(
clean_function_name(&format!(
"some::GenericThing<_, _>::function_name{}",
USELESS_SCOPE_NAME_SUFFIX
)),
"GenericThing<_, _>::function_name"
);
assert_eq!(
clean_function_name(&format!(
"<some::ConcreteType as some::bloody::Trait>::function_name{}",
USELESS_SCOPE_NAME_SUFFIX
)),
"<ConcreteType as Trait>::function_name"
);
}