1use syn::*;
2
3pub fn type_name<T: ?Sized + 'static>() -> &'static str {
14 use std::any::TypeId;
15 use std::cell::RefCell;
16 use std::collections::HashMap;
17 use std::collections::hash_map::Entry;
18
19 thread_local! {
20 static TYPE_NAME_CACHE: RefCell<HashMap<TypeId, &'static str>> =
21 RefCell::new(HashMap::new());
22 }
23
24 TYPE_NAME_CACHE.with_borrow_mut(|cache| match cache.entry(TypeId::of::<T>()) {
25 Entry::Occupied(entry) =>
26 *entry.get(),
27 Entry::Vacant(entry) =>
28 *entry.insert(type_name_internal::<T>()),
29 })
30}
31
32pub fn type_name_of_val<T: ?Sized + 'static>(_: &T) -> &'static str {
42 type_name::<T>()
43}
44
45fn type_name_internal<T: ?Sized + 'static>() -> &'static str {
46 let type_name = std::any::type_name::<T>();
47 let Ok(mut ty) = syn::parse_str::<Type>(type_name) else {
48 return "<error>";
49 };
50
51 truncate_type(&mut ty);
52
53 use quote::quote;
56 use rust_format::Formatter as _;
57 let format_result =
58 rust_format::RustFmt::default()
59 .format_tokens(quote!(fn main() -> #ty {}))
60 .unwrap_or("<error>".to_string());
61 let start = const { "fn main() -> ".len() };
62 let end = format_result.len() - const { " {}\r\n".len() };
63 Box::leak(
64 format_result[start..end]
65 .to_owned()
66 .into_boxed_str())
67}
68
69fn truncate_type(ty: &mut Type) {
70 match *ty {
71 Type::Infer(_) |
72 Type::Macro(_) |
73 Type::Never(_) |
74 Type::Verbatim(_) => {}
75
76 Type::Array(TypeArray { ref mut elem, .. }) |
77 Type::Group(TypeGroup { group_token: _, ref mut elem }) |
78 Type::Paren(TypeParen { paren_token: _, ref mut elem }) |
79 Type::Ptr(TypePtr { ref mut elem, .. }) |
80 Type::Slice(TypeSlice { ref mut elem, .. }) => truncate_type(elem),
81
82 Type::Reference(TypeReference {
83 ref mut lifetime,
84 ref mut elem,
85 ..
86 }) => {
87 *lifetime = None;
88 truncate_type(elem);
89 }
90
91 Type::Path(ref mut ty) => truncate_path(&mut ty.path),
92
93 Type::BareFn(ref mut ty) => {
94 for input in ty.inputs.iter_mut() {
95 truncate_type(&mut input.ty);
96 }
97
98 if let ReturnType::Type(_, ref mut ty) = ty.output {
99 truncate_type(ty.as_mut());
100 }
101 }
102
103 Type::ImplTrait(ref mut ty) => {
104 for bound in ty.bounds.iter_mut() {
105 if let &mut TypeParamBound::Trait(ref mut trt) = bound {
106 truncate_path(&mut trt.path);
107 }
108 }
109 }
110
111 Type::TraitObject(ref mut ty) => {
112 for bound in ty.bounds.iter_mut() {
113 if let &mut TypeParamBound::Trait(ref mut trt) = bound {
114 truncate_path(&mut trt.path);
115 }
116 }
117 }
118
119 Type::Tuple(ref mut ty) => {
120 for elem in ty.elems.iter_mut() {
121 truncate_type(elem);
122 }
123 }
124
125 _ => { }
126 }
127}
128
129fn truncate_path(path: &mut Path) {
130 let path_mut = path;
131 let path = std::mem::replace(
132 path_mut,
133 Path {
134 leading_colon: None,
135 segments: Default::default(),
136 });
137
138 let Some(mut last_segment) = path.segments.into_iter().last() else {
139 path_mut.leading_colon = None;
140 path_mut.segments = Default::default();
141 return;
142 };
143
144 match last_segment.arguments {
145 PathArguments::None => {}
146 PathArguments::AngleBracketed(ref mut args) => {
147 for arg in args.args.iter_mut() {
148 match *arg {
149 GenericArgument::Type(ref mut ty) => truncate_type(ty),
150 GenericArgument::AssocType(ref mut ty) => {
151 truncate_type(&mut ty.ty)
152 }
153 _ => {}
154 }
155 }
156 }
157 PathArguments::Parenthesized(ref mut args) => {
158 for input in args.inputs.iter_mut() {
159 truncate_type(input);
160 }
161 if let ReturnType::Type(_, ref mut output) = args.output {
162 truncate_type(output);
163 }
164 }
165 }
166
167 path_mut.leading_colon = None;
168 path_mut.segments = Some(last_segment).into_iter().collect();
169}
170
171#[cfg(test)]
172mod test {
173 use super::type_name;
174
175 #[test]
176 fn test_type_name() {
177 assert_eq!(type_name::<i32>(), "i32");
179 assert_eq!(type_name::<bool>(), "bool");
180
181 assert_eq!(type_name::<str>(), "str");
183 assert_eq!(type_name::<[i32]>(), "[i32]");
184
185 assert_eq!(type_name::<&i32>(), "&i32");
187 assert_eq!(type_name::<&str>(), "&str");
188 assert_eq!(type_name::<&'static str>(), "&str");
190 assert_eq!(type_name::<&&&str>(), "&&&str");
192 assert_eq!(type_name::<&[i32]>(), "&[i32]");
194
195 assert_eq!(type_name::<&mut String>(), "&mut String");
197 assert_eq!(type_name::<&mut &str>(), "&mut &str");
198 assert_eq!(type_name::<&mut str>(), "&mut str");
199 assert_eq!(type_name::<&mut [i32]>(), "&mut [i32]");
200
201 assert_eq!(type_name::<*const i32>(), "*const i32");
203 assert_eq!(type_name::<*mut i32>(), "*mut i32");
204 assert_eq!(type_name::<*const str>(), "*const str");
205 assert_eq!(type_name::<*mut [u8]>(), "*mut [u8]");
206 assert_eq!(type_name::<*const *mut i32>(), "*const *mut i32");
208 assert_eq!(type_name::<*const &str>(), "*const &str");
210 assert_eq!(type_name::<&*const i32>(), "&*const i32");
211
212 assert_eq!(type_name::<[i32; 5]>(), "[i32; 5]");
214 assert_eq!(type_name::<[bool; 0]>(), "[bool; 0]");
215 assert_eq!(type_name::<&[i32; 3]>(), "&[i32; 3]");
216 assert_eq!(type_name::<&mut [i32; 5]>(), "&mut [i32; 5]");
217 assert_eq!(type_name::<[[i32; 2]; 3]>(), "[[i32; 2]; 3]");
219 assert_eq!(type_name::<[[[u8; 2]; 3]; 4]>(), "[[[u8; 2]; 3]; 4]");
220 assert_eq!(type_name::<[(i32, bool); 10]>(), "[(i32, bool); 10]");
222
223 assert_eq!(type_name::<()>(), "()");
225 assert_eq!(type_name::<(i32,)>(), "(i32,)");
226 assert_eq!(type_name::<(i32, String, bool)>(), "(i32, String, bool)");
227 assert_eq!(type_name::<(i32, (String, bool))>(), "(i32, (String, bool))");
229 assert_eq!(type_name::<(&str, &[u8])>(), "(&str, &[u8])");
231 assert_eq!(type_name::<(&mut String, &i32)>(), "(&mut String, &i32)");
232
233 assert_eq!(type_name::<Option<i32>>(), "Option<i32>");
235 assert_eq!(type_name::<Option<&str>>(), "Option<&str>");
236 assert_eq!(type_name::<Result<i32, String>>(), "Result<i32, String>");
237 assert_eq!(type_name::<Result<(), ()>>(), "Result<(), ()>");
238 assert_eq!(type_name::<Vec<i32>>(), "Vec<i32>");
239 assert_eq!(type_name::<std::collections::HashMap<String, i32>>(), "HashMap<String, i32>");
240 assert_eq!(type_name::<std::collections::BTreeMap<String, i32>>(), "BTreeMap<String, i32>");
241
242 assert_eq!(type_name::<fn()>(), "fn()");
244 assert_eq!(type_name::<fn(i32) -> i32>(), "fn(i32) -> i32");
245 assert_eq!(type_name::<fn(i32, String, bool)>(), "fn(i32, String, bool)");
246 assert_eq!(type_name::<fn(&str) -> String>(), "fn(&str) -> String");
247 assert_eq!(type_name::<fn(&mut i32)>(), "fn(&mut i32)");
248 assert_eq!(type_name::<fn(*const i32) -> *mut i32>(), "fn(*const i32) -> *mut i32");
249 assert_eq!(type_name::<fn() -> fn(i32) -> i32>(), "fn() -> fn(i32) -> i32");
251 assert_eq!(type_name::<fn(fn(i32) -> i32) -> i32>(), "fn(fn(i32) -> i32) -> i32");
252 assert_eq!(type_name::<unsafe fn()>(), "unsafe fn()");
254 assert_eq!(type_name::<extern "C" fn(i32) -> i32>(), "extern \"C\" fn(i32) -> i32");
255 assert_eq!(type_name::<unsafe extern "C" fn(i32)>(), "unsafe extern \"C\" fn(i32)");
256
257 assert_eq!(type_name::<Box<dyn std::fmt::Debug>>(), "Box<dyn Debug>");
259 assert_eq!(type_name::<&dyn std::fmt::Display>(), "&dyn Display");
260 assert_eq!(type_name::<&mut dyn std::io::Write>(), "&mut dyn Write");
261 assert_eq!(type_name::<Box<dyn std::fmt::Debug + Send>>(), "Box<dyn Debug + Send>");
262 assert_eq!(type_name::<Box<dyn std::fmt::Debug + Send + Sync>>(), "Box<dyn Debug + Send + Sync>");
263 assert_eq!(type_name::<dyn std::fmt::Debug>(), "dyn Debug");
264 assert_eq!(type_name::<dyn std::fmt::Debug + Send>(), "dyn Debug + Send");
265
266 assert_eq!(type_name::<Box<i32>>(), "Box<i32>");
268 assert_eq!(type_name::<Box<str>>(), "Box<str>");
269 assert_eq!(type_name::<Box<[i32]>>(), "Box<[i32]>");
270 assert_eq!(type_name::<std::rc::Rc<String>>(), "Rc<String>");
271 assert_eq!(type_name::<std::sync::Arc<String>>(), "Arc<String>");
272 assert_eq!(type_name::<std::cell::RefCell<i32>>(), "RefCell<i32>");
273
274 assert_eq!(type_name::<Vec<Vec<String>>>(), "Vec<Vec<String>>");
276 assert_eq!(type_name::<Vec<Vec<Vec<i32>>>>(), "Vec<Vec<Vec<i32>>>");
277 assert_eq!(type_name::<Option<Result<i32, String>>>(), "Option<Result<i32, String>>");
278 assert_eq!(type_name::<Box<Option<Vec<String>>>>(), "Box<Option<Vec<String>>>");
279 assert_eq!(type_name::<Option<Box<dyn std::fmt::Debug>>>(), "Option<Box<dyn Debug>>");
280 assert_eq!(type_name::<Vec<Option<&str>>>(), "Vec<Option<&str>>");
281
282 assert_eq!(type_name::<(Option<i32>, Result<String, ()>)>(), "(Option<i32>, Result<String, ()>)");
284 assert_eq!(type_name::<&[(i32, String)]>(), "&[(i32, String)]");
285 assert_eq!(type_name::<[(Option<i32>, &str); 5]>(), "[(Option<i32>, &str); 5]");
286 assert_eq!(type_name::<std::collections::HashMap<String, Vec<i32>>>(), "HashMap<String, Vec<i32>>");
287 assert_eq!(type_name::<&[Option<Result<i32, String>>]>(), "&[Option<Result<i32, String>>]");
288 assert_eq!(type_name::<Vec<fn(i32) -> i32>>(), "Vec<fn(i32) -> i32>");
290 assert_eq!(type_name::<Option<fn() -> String>>(), "Option<fn() -> String>");
291
292 assert_eq!(type_name::<std::vec::Vec<i32>>(), "Vec<i32>");
294 assert_eq!(type_name::<std::string::String>(), "String");
295 assert_eq!(type_name::<std::boxed::Box<i32>>(), "Box<i32>");
296 assert_eq!(type_name::<Result<Vec<u8>, std::io::Error>>(), "Result<Vec<u8>, Error>");
298 assert_eq!(type_name::<std::collections::HashMap<std::string::String, std::vec::Vec<i32>>>(), "HashMap<String, Vec<i32>>");
299
300 assert_eq!(type_name::<Vec<Option<Result<Box<dyn std::fmt::Debug>, String>>>>(), "Vec<Option<Result<Box<dyn Debug>, String>>>");
302 assert_eq!(type_name::<&[Option<&[(i32, &str)]>]>(), "&[Option<&[(i32, &str)]>]");
303 assert_eq!(type_name::<fn(Vec<&str>) -> Option<Result<i32, Box<dyn std::error::Error>>>>(), "fn(Vec<&str>) -> Option<Result<i32, Box<dyn Error>>>");
304
305 assert_eq!(type_name::<[(); 5]>(), "[(); 5]");
307 assert_eq!(type_name::<std::marker::PhantomData<i32>>(), "PhantomData<i32>");
308 assert_eq!(type_name::<std::marker::PhantomData<&str>>(), "PhantomData<&str>");
309 }
310}