diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index f793ab6f8fca4..97e84fef3a56f 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -231,7 +231,7 @@ impl TypeRegistryArc { /// a [`TypeData`] which can be used to downcast [`Reflect`] trait objects of /// this type to trait objects of the relevant trait. /// -/// [short name]: TypeRegistration::get_short_name +/// [short name]: bevy_utils::get_short_name /// [`TypeInfo`]: crate::TypeInfo /// [0]: crate::Reflect /// [1]: crate::Reflect @@ -287,14 +287,14 @@ impl TypeRegistration { let type_name = std::any::type_name::(); Self { data: HashMap::default(), - short_name: Self::get_short_name(type_name), + short_name: bevy_utils::get_short_name(type_name), type_info: T::type_info(), } } /// Returns the [short name] of the type. /// - /// [short name]: TypeRegistration::get_short_name + /// [short name]: bevy_utils::get_short_name pub fn short_name(&self) -> &str { &self.short_name } @@ -305,49 +305,6 @@ impl TypeRegistration { pub fn type_name(&self) -> &'static str { self.type_info.type_name() } - - /// Calculates the short name of a type. - /// - /// The short name of a type is its full name as returned by - /// [`std::any::type_name`], but with the prefix of all paths removed. For - /// example, the short name of `alloc::vec::Vec>` - /// would be `Vec>`. - pub fn get_short_name(full_name: &str) -> String { - let mut short_name = String::new(); - - { - // A typename may be a composition of several other type names (e.g. generic parameters) - // separated by the characters that we try to find below. - // Then, each individual typename is shortened to its last path component. - // - // Note: Instead of `find`, `split_inclusive` would be nice but it's still unstable... - let mut remainder = full_name; - while let Some(index) = remainder.find(&['<', '>', '(', ')', '[', ']', ',', ';'][..]) { - let (path, new_remainder) = remainder.split_at(index); - // Push the shortened path in front of the found character - short_name.push_str(path.rsplit(':').next().unwrap()); - // Push the character that was found - let character = new_remainder.chars().next().unwrap(); - short_name.push(character); - // Advance the remainder - if character == ',' || character == ';' { - // A comma or semicolon is always followed by a space - short_name.push(' '); - remainder = &new_remainder[2..]; - } else { - remainder = &new_remainder[1..]; - } - } - - // The remainder will only be non-empty if there were no matches at all - if !remainder.is_empty() { - // Then, the full typename is a path that has to be shortened - short_name.push_str(remainder.rsplit(':').next().unwrap()); - } - } - - short_name - } } impl Clone for TypeRegistration { @@ -459,42 +416,6 @@ mod test { use crate::TypeRegistration; use bevy_utils::HashMap; - #[test] - fn test_get_short_name() { - assert_eq!( - TypeRegistration::get_short_name(std::any::type_name::()), - "f64" - ); - assert_eq!( - TypeRegistration::get_short_name(std::any::type_name::()), - "String" - ); - assert_eq!( - TypeRegistration::get_short_name(std::any::type_name::<(u32, f64)>()), - "(u32, f64)" - ); - assert_eq!( - TypeRegistration::get_short_name(std::any::type_name::<(String, String)>()), - "(String, String)" - ); - assert_eq!( - TypeRegistration::get_short_name(std::any::type_name::<[f64]>()), - "[f64]" - ); - assert_eq!( - TypeRegistration::get_short_name(std::any::type_name::<[String]>()), - "[String]" - ); - assert_eq!( - TypeRegistration::get_short_name(std::any::type_name::<[f64; 16]>()), - "[f64; 16]" - ); - assert_eq!( - TypeRegistration::get_short_name(std::any::type_name::<[String; 16]>()), - "[String; 16]" - ); - } - #[test] fn test_property_type_registration() { assert_eq!( diff --git a/crates/bevy_utils/src/lib.rs b/crates/bevy_utils/src/lib.rs index 0f425be909b95..2b09fdbf0f3b2 100644 --- a/crates/bevy_utils/src/lib.rs +++ b/crates/bevy_utils/src/lib.rs @@ -4,6 +4,8 @@ pub mod prelude { pub mod futures; pub mod label; +mod short_names; +pub use short_names::get_short_name; mod default; mod float_ord; diff --git a/crates/bevy_utils/src/short_names.rs b/crates/bevy_utils/src/short_names.rs new file mode 100644 index 0000000000000..1c30bf61f9d0f --- /dev/null +++ b/crates/bevy_utils/src/short_names.rs @@ -0,0 +1,110 @@ +/// Shortens a type name to remove all module paths. +/// +/// The short name of a type is its full name as returned by +/// [`std::any::type_name`], but with the prefix of all paths removed. For +/// example, the short name of `alloc::vec::Vec>` +/// would be `Vec>`. +pub fn get_short_name(full_name: &str) -> String { + // Generics result in nested paths within <..> blocks. + // Consider "bevy_render::camera::camera::extract_cameras". + // To tackle this, we parse the string from left to right, collapsing as we go. + let mut index: usize = 0; + let end_of_string = full_name.len(); + let mut parsed_name = String::new(); + + while index < end_of_string { + let rest_of_string = full_name.get(index..end_of_string).unwrap_or_default(); + + // Collapse everything up to the next special character, + // then skip over it + if let Some(special_character_index) = rest_of_string.find(|c: char| { + (c == ' ') + || (c == '<') + || (c == '>') + || (c == '(') + || (c == ')') + || (c == '[') + || (c == ']') + || (c == ',') + || (c == ';') + }) { + let segment_to_collapse = rest_of_string + .get(0..special_character_index) + .unwrap_or_default(); + parsed_name += collapse_type_name(segment_to_collapse); + // Insert the special character + let special_character = + &rest_of_string[special_character_index..=special_character_index]; + parsed_name.push_str(special_character); + // Move the index just past the special character + index += special_character_index + 1; + } else { + // If there are no special characters left, we're done! + parsed_name += collapse_type_name(rest_of_string); + index = end_of_string; + } + } + parsed_name +} + +#[inline(always)] +fn collapse_type_name(string: &str) -> &str { + string.split("::").last().unwrap() +} + +#[cfg(test)] +mod name_formatting_tests { + use super::get_short_name; + + #[test] + fn trivial() { + assert_eq!(get_short_name("test_system"), "test_system"); + } + + #[test] + fn path_seperated() { + assert_eq!( + get_short_name("bevy_prelude::make_fun_game"), + "make_fun_game".to_string() + ); + } + + #[test] + fn tuple_type() { + assert_eq!( + get_short_name("(String, String)"), + "(String, String)".to_string() + ); + } + + #[test] + fn array_type() { + assert_eq!(get_short_name("[i32; 3]"), "[i32; 3]".to_string()); + } + + #[test] + fn trivial_generics() { + assert_eq!(get_short_name("a"), "a".to_string()); + } + + #[test] + fn multiple_type_parameters() { + assert_eq!(get_short_name("a"), "a".to_string()); + } + + #[test] + fn generics() { + assert_eq!( + get_short_name("bevy_render::camera::camera::extract_cameras"), + "extract_cameras".to_string() + ); + } + + #[test] + fn nested_generics() { + assert_eq!( + get_short_name("bevy::mad_science::do_mad_science, bavy::TypeSystemAbuse>"), + "do_mad_science, TypeSystemAbuse>".to_string() + ); + } +} diff --git a/tools/spancmp/Cargo.toml b/tools/spancmp/Cargo.toml index 5b2b0c5749e92..aac07a306ef5a 100644 --- a/tools/spancmp/Cargo.toml +++ b/tools/spancmp/Cargo.toml @@ -12,5 +12,5 @@ serde = { version = "1.0", features = ["derive"] } clap = { version = "3.2", features = ["derive"] } regex = "1.5" termcolor = "1.1" -bevy_reflect = { path = "../../crates/bevy_reflect", version = "0.8.0-dev" } +bevy_utils = { path = "../../crates/bevy_utils", version = "0.8.0-dev" } lazy_static = "1.4" diff --git a/tools/spancmp/src/pretty.rs b/tools/spancmp/src/pretty.rs index e51d453312ba8..730393a81c646 100644 --- a/tools/spancmp/src/pretty.rs +++ b/tools/spancmp/src/pretty.rs @@ -1,4 +1,4 @@ -use bevy_reflect::TypeRegistration; +use bevy_utils::get_short_name; use lazy_static::lazy_static; use regex::Regex; use termcolor::{Color, ColorSpec, StandardStream, WriteColor}; @@ -206,21 +206,18 @@ lazy_static! { pub fn simplify_name(name: &str) -> String { if let Some(captures) = SYSTEM_NAME.captures(name) { - return format!( - r#"system: name="{}""#, - TypeRegistration::get_short_name(&captures[1]) - ); + return format!(r#"system: name="{}""#, get_short_name(&captures[1])); } if let Some(captures) = SYSTEM_OVERHEAD.captures(name) { return format!( r#"system overhead: name="{}""#, - TypeRegistration::get_short_name(&captures[1]) + get_short_name(&captures[1]) ); } if let Some(captures) = SYSTEM_COMMANDS.captures(name) { return format!( r#"system_commands: name="{}""#, - TypeRegistration::get_short_name(&captures[1]) + get_short_name(&captures[1]) ); } name.to_string()