disqualified/
short_name.rs1#[cfg_attr(
10 feature = "alloc",
11 doc = r#" To get a [`String`] from this type, use the [`to_string`](`alloc::string::ToString::to_string`) method."#
12)]
13#[derive(Clone, Copy)]
28pub struct ShortName<'a>(pub &'a str);
29
30impl ShortName<'static> {
31 pub fn of<T: ?Sized>() -> Self {
33 Self(core::any::type_name::<T>())
34 }
35}
36
37impl<'a> ShortName<'a> {
38 pub const fn original(&self) -> &'a str {
40 self.0
41 }
42}
43
44impl<'a> From<&'a str> for ShortName<'a> {
45 fn from(value: &'a str) -> Self {
46 Self(value)
47 }
48}
49
50impl<'a> core::fmt::Debug for ShortName<'a> {
51 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
52 let &ShortName(full_name) = self;
53 let mut index: usize = 0;
57 let end_of_string = full_name.len();
58
59 while index < end_of_string {
60 let rest_of_string = full_name.get(index..end_of_string).unwrap_or_default();
61
62 if let Some(special_character_index) = rest_of_string.find(|c: char| {
65 (c == ' ')
66 || (c == '<')
67 || (c == '>')
68 || (c == '(')
69 || (c == ')')
70 || (c == '[')
71 || (c == ']')
72 || (c == ',')
73 || (c == ';')
74 }) {
75 let segment_to_collapse = rest_of_string
76 .get(0..special_character_index)
77 .unwrap_or_default();
78
79 f.write_str(collapse_type_name(segment_to_collapse))?;
80
81 let special_character =
83 &rest_of_string[special_character_index..=special_character_index];
84
85 f.write_str(special_character)?;
86
87 match special_character {
88 ">" | ")" | "]"
89 if rest_of_string[special_character_index + 1..].starts_with("::") =>
90 {
91 f.write_str("::")?;
92 index += special_character_index + 3;
94 }
95 _ => index += special_character_index + 1,
97 }
98 } else {
99 f.write_str(collapse_type_name(rest_of_string))?;
101 index = end_of_string;
102 }
103 }
104
105 Ok(())
106 }
107}
108
109impl<'a> core::fmt::Display for ShortName<'a> {
110 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
111 <Self as core::fmt::Debug>::fmt(self, f)
112 }
113}
114
115#[inline(always)]
116fn collapse_type_name(string: &str) -> &str {
117 let mut segments = string.rsplit("::");
120 let (last, second_last): (&str, Option<&str>) = (segments.next().unwrap(), segments.next());
121 let Some(second_last) = second_last else {
122 return last;
123 };
124
125 if second_last.starts_with(char::is_uppercase) {
126 let index = string.len() - last.len() - second_last.len() - 2;
127 &string[index..]
128 } else {
129 last
130 }
131}
132
133#[cfg(all(test, feature = "alloc"))]
134mod name_formatting_tests {
135 use super::ShortName;
136
137 #[test]
138 fn trivial() {
139 assert_eq!(ShortName("test_system").to_string(), "test_system");
140 }
141
142 #[test]
143 fn path_separated() {
144 assert_eq!(
145 ShortName("bevy_prelude::make_fun_game").to_string(),
146 "make_fun_game"
147 );
148 }
149
150 #[test]
151 fn tuple_type() {
152 assert_eq!(
153 ShortName("(String, String)").to_string(),
154 "(String, String)"
155 );
156 }
157
158 #[test]
159 fn array_type() {
160 assert_eq!(ShortName("[i32; 3]").to_string(), "[i32; 3]");
161 }
162
163 #[test]
164 fn trivial_generics() {
165 assert_eq!(ShortName("a<B>").to_string(), "a<B>");
166 }
167
168 #[test]
169 fn multiple_type_parameters() {
170 assert_eq!(ShortName("a<B, C>").to_string(), "a<B, C>");
171 }
172
173 #[test]
174 fn enums() {
175 assert_eq!(ShortName("Option::None").to_string(), "Option::None");
176 assert_eq!(ShortName("Option::Some(2)").to_string(), "Option::Some(2)");
177 assert_eq!(
178 ShortName("bevy_render::RenderSet::Prepare").to_string(),
179 "RenderSet::Prepare"
180 );
181 }
182
183 #[test]
184 fn generics() {
185 assert_eq!(
186 ShortName("bevy_render::camera::camera::extract_cameras<bevy_render::camera::bundle::Camera3d>").to_string(),
187 "extract_cameras<Camera3d>"
188 );
189 }
190
191 #[test]
192 fn nested_generics() {
193 assert_eq!(
194 ShortName("bevy::mad_science::do_mad_science<mad_science::Test<mad_science::Tube>, bavy::TypeSystemAbuse>").to_string(),
195 "do_mad_science<Test<Tube>, TypeSystemAbuse>"
196 );
197 }
198
199 #[test]
200 fn sub_path_after_closing_bracket() {
201 assert_eq!(
202 ShortName("bevy_asset::assets::Assets<bevy_scene::dynamic_scene::DynamicScene>::asset_event_system").to_string(),
203 "Assets<DynamicScene>::asset_event_system"
204 );
205 assert_eq!(
206 ShortName("(String, String)::default").to_string(),
207 "(String, String)::default"
208 );
209 assert_eq!(
210 ShortName("[i32; 16]::default").to_string(),
211 "[i32; 16]::default"
212 );
213 }
214}