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)]
27pub struct ShortName<'a>(pub &'a str);
28
29impl ShortName<'static> {
30 pub fn of<T: ?Sized>() -> Self {
32 Self(core::any::type_name::<T>())
33 }
34}
35
36impl<'a> ShortName<'a> {
37 pub const fn original(&self) -> &'a str {
39 self.0
40 }
41}
42
43impl<'a> From<&'a str> for ShortName<'a> {
44 fn from(value: &'a str) -> Self {
45 Self(value)
46 }
47}
48
49impl<'a> core::fmt::Debug for ShortName<'a> {
50 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51 let &ShortName(full_name) = self;
52 let mut index: usize = 0;
56 let end_of_string = full_name.len();
57
58 while index < end_of_string {
59 let rest_of_string = full_name.get(index..end_of_string).unwrap_or_default();
60
61 if let Some(special_character_index) = rest_of_string.find(|c: char| {
64 (c == ' ')
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 if special_character == "<" {
86 let mut end_lifetime_index = special_character_index + 1;
87 while rest_of_string[end_lifetime_index..].starts_with("'_, ") {
89 end_lifetime_index += 4;
90 }
91 if rest_of_string[end_lifetime_index..].starts_with("'_>") {
92 end_lifetime_index += 3;
95 } else {
96 f.write_str("<")?;
97 }
98 index += end_lifetime_index;
99 continue;
100 }
101
102 f.write_str(special_character)?;
103
104 match special_character {
105 ">" | ")" | "]"
106 if rest_of_string[special_character_index + 1..].starts_with("::") =>
107 {
108 f.write_str("::")?;
109 index += special_character_index + 3;
111 }
112 _ => index += special_character_index + 1,
114 }
115 } else {
116 f.write_str(collapse_type_name(rest_of_string))?;
118 index = end_of_string;
119 }
120 }
121
122 Ok(())
123 }
124}
125
126impl<'a> core::fmt::Display for ShortName<'a> {
127 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
128 <Self as core::fmt::Debug>::fmt(self, f)
129 }
130}
131
132#[inline(always)]
133fn collapse_type_name(string: &str) -> &str {
134 let mut closure_start = string.len();
138 let mut closure_end = closure_start;
139 if let Some(end_index) = string.find("}}") {
140 if let Some(start_index) = string[..end_index].find("::{{") {
141 closure_end = end_index + 2;
142 closure_start = start_index;
143 }
144 }
145
146 let mut segments = string[..closure_start].rsplit("::");
150 let last = segments.next().unwrap();
151 let second_last = segments.next();
152 let type_len = second_last
153 .filter(|second_last| second_last.starts_with(char::is_uppercase))
154 .map_or(0, |second_last| second_last.len() + 2);
155 let index = closure_start - last.len() - type_len;
156 &string[index..closure_end]
157}
158
159#[cfg(all(test, feature = "alloc"))]
160mod name_formatting_tests {
161 use super::ShortName;
162
163 #[test]
164 fn trivial() {
165 assert_eq!(ShortName("test_system").to_string(), "test_system");
166 }
167
168 #[test]
169 fn empty() {
170 assert_eq!(ShortName("").to_string(), "");
171 assert_eq!(ShortName("::").to_string(), "");
172 }
173
174 #[test]
175 fn path_separated() {
176 assert_eq!(
177 ShortName("bevy_prelude::make_fun_game").to_string(),
178 "make_fun_game"
179 );
180 }
181
182 #[test]
183 fn tuple_type() {
184 assert_eq!(
185 ShortName("(String, String)").to_string(),
186 "(String, String)"
187 );
188 }
189
190 #[test]
191 fn array_type() {
192 assert_eq!(ShortName("[i32; 3]").to_string(), "[i32; 3]");
193 }
194
195 #[test]
196 fn trivial_generics() {
197 assert_eq!(ShortName("a<B>").to_string(), "a<B>");
198 }
199
200 #[test]
201 fn multiple_type_parameters() {
202 assert_eq!(ShortName("a<B, C>").to_string(), "a<B, C>");
203 }
204
205 #[test]
206 fn enums() {
207 assert_eq!(ShortName("Option::None").to_string(), "Option::None");
208 assert_eq!(ShortName("Option::Some(2)").to_string(), "Option::Some(2)");
209 assert_eq!(
210 ShortName("bevy_render::RenderSet::Prepare").to_string(),
211 "RenderSet::Prepare"
212 );
213 }
214
215 #[test]
216 fn generics() {
217 assert_eq!(
218 ShortName("bevy_render::camera::camera::extract_cameras<bevy_render::camera::bundle::Camera3d>").to_string(),
219 "extract_cameras<Camera3d>"
220 );
221 }
222
223 #[test]
224 fn nested_generics() {
225 assert_eq!(
226 ShortName("bevy::mad_science::do_mad_science<mad_science::Test<mad_science::Tube>, bavy::TypeSystemAbuse>").to_string(),
227 "do_mad_science<Test<Tube>, TypeSystemAbuse>"
228 );
229 }
230
231 #[test]
232 fn sub_path_after_closing_bracket() {
233 assert_eq!(
234 ShortName("bevy_asset::assets::Assets<bevy_scene::dynamic_scene::DynamicScene>::asset_event_system").to_string(),
235 "Assets<DynamicScene>::asset_event_system"
236 );
237 assert_eq!(
238 ShortName("(String, String)::default").to_string(),
239 "(String, String)::default"
240 );
241 assert_eq!(
242 ShortName("[i32; 16]::default").to_string(),
243 "[i32; 16]::default"
244 );
245 }
246
247 #[test]
248 fn references() {
249 assert_eq!(ShortName("&T").to_string(), "&T");
250 assert_eq!(ShortName("&mut T").to_string(), "&mut T");
251 assert_eq!(ShortName("&t::T<&u::U>").to_string(), "&T<&U>");
252 assert_eq!(
253 ShortName("&mut t::T<&mut u::U>").to_string(),
254 "&mut T<&mut U>"
255 );
256 }
257
258 #[test]
259 fn pointers() {
260 assert_eq!(ShortName("*const T").to_string(), "*const T");
261 assert_eq!(ShortName("*mut T").to_string(), "*mut T");
262 assert_eq!(
263 ShortName("*const t::T<*const u::U>").to_string(),
264 "*const T<*const U>"
265 );
266 assert_eq!(
267 ShortName("*mut t::T<*mut u::U>").to_string(),
268 "*mut T<*mut U>"
269 );
270 }
271
272 #[test]
273 fn lifetimes() {
274 assert_eq!(ShortName("t::T<'_>").to_string(), "T");
275 assert_eq!(ShortName("t::T<'_, '_>").to_string(), "T");
276 assert_eq!(ShortName("t::T<'_, '_, '_>").to_string(), "T");
277 assert_eq!(ShortName("t::T<'_, u::U>").to_string(), "T<U>");
278 assert_eq!(ShortName("t::T<'_, '_, u::U>").to_string(), "T<U>");
279 assert_eq!(ShortName("t::T<'_, '_, '_, u::U>").to_string(), "T<U>");
280 }
281
282 #[test]
283 fn functions() {
284 assert_eq!(ShortName("m::f").to_string(), "f");
285 assert_eq!(ShortName("m::f::{{closure}}").to_string(), "f::{{closure}}");
286 assert_eq!(ShortName("m::Ty::f").to_string(), "Ty::f");
287 assert_eq!(
288 ShortName("m::Ty::f::{{closure}}").to_string(),
289 "Ty::f::{{closure}}"
290 );
291 assert_eq!(ShortName("m::Ty<t::T>::f").to_string(), "Ty<T>::f");
292 assert_eq!(
293 ShortName("m::Ty<t::T>::f::{{closure}}").to_string(),
294 "Ty<T>::f::{{closure}}"
295 );
296
297 assert_eq!(ShortName("m::f<t::T>").to_string(), "f<T>");
298 assert_eq!(
299 ShortName("m::f<t::T>::{{closure}}").to_string(),
300 "f<T>::{{closure}}"
301 );
302 assert_eq!(ShortName("m::Ty::f<t::T>").to_string(), "Ty::f<T>");
303 assert_eq!(
304 ShortName("m::Ty::f<t::T>::{{closure}}").to_string(),
305 "Ty::f<T>::{{closure}}"
306 );
307 assert_eq!(ShortName("m::Ty<t::T>::f<t::T>").to_string(), "Ty<T>::f<T>");
308 assert_eq!(
309 ShortName("m::Ty<t::T>::f<t::T>::{{closure}}").to_string(),
310 "Ty<T>::f<T>::{{closure}}"
311 );
312
313 assert_eq!(
314 ShortName("m::f::{{closure}}::{{closure}}").to_string(),
315 "f::{{closure}}"
316 );
317 assert_eq!(
318 ShortName("m::f::{{closure}}::{{closure}}::{{call_once}}").to_string(),
319 "f::{{closure}}"
320 );
321 assert_eq!(
322 ShortName("m::Ty::f::{{closure}}::{{closure}}").to_string(),
323 "Ty::f::{{closure}}"
324 );
325 assert_eq!(
326 ShortName("m::Ty::f::{{closure}}::{{closure}}::{{call_once}}").to_string(),
327 "Ty::f::{{closure}}"
328 );
329 }
330}