1use std::any::TypeId;
2use std::collections::BTreeMap;
3use std::sync::LazyLock;
4
5use parking_lot::{RwLock, RwLockReadGuard};
6use regex::Regex;
7
8#[derive(Default, Debug, Clone, Copy)]
10pub enum NameRule {
11 #[default]
13 Auto,
14 Force(&'static str),
16}
17
18static GLOBAL_NAMER: LazyLock<RwLock<Box<dyn Namer>>> =
19 LazyLock::new(|| RwLock::new(Box::new(FlexNamer::new())));
20static NAME_TYPES: LazyLock<RwLock<BTreeMap<String, (TypeId, &'static str)>>> =
21 LazyLock::new(Default::default);
22
23pub fn set_namer(namer: impl Namer) {
43 *GLOBAL_NAMER.write() = Box::new(namer);
44 NAME_TYPES.write().clear();
45}
46
47#[doc(hidden)]
48pub fn namer() -> RwLockReadGuard<'static, Box<dyn Namer>> {
49 GLOBAL_NAMER.read()
50}
51
52pub fn type_info_by_name(name: &str) -> Option<(TypeId, &'static str)> {
54 NAME_TYPES.read().get(name).cloned()
55}
56
57pub fn set_name_type_info(
59 name: String,
60 type_id: TypeId,
61 type_name: &'static str,
62) -> Option<(TypeId, &'static str)> {
63 NAME_TYPES.write().insert(name, (type_id, type_name))
64}
65
66pub fn assign_name<T: 'static>(rule: NameRule) -> String {
70 let type_id = TypeId::of::<T>();
71 let type_name = std::any::type_name::<T>();
72 for (name, (exist_id, _)) in NAME_TYPES.read().iter() {
73 if *exist_id == type_id {
74 return name.clone();
75 }
76 }
77 namer().assign_name(type_id, type_name, rule)
78}
79
80pub fn get_name<T: 'static>() -> String {
82 let type_id = TypeId::of::<T>();
83 for (name, (exist_id, _)) in NAME_TYPES.read().iter() {
84 if *exist_id == type_id {
85 return name.clone();
86 }
87 }
88 panic!(
89 "Type not found in the name registry: {:?}",
90 std::any::type_name::<T>()
91 );
92}
93
94fn type_generic_part(type_name: &str) -> String {
95 let re = Regex::new(r"^[^<]+").expect("Invalid regex");
96 let result = re.replace_all(type_name, "");
97 result.to_string()
98}
99pub trait Namer: Sync + Send + 'static {
101 fn assign_name(&self, type_id: TypeId, type_name: &'static str, rule: NameRule) -> String;
103}
104
105#[derive(Default, Clone, Debug)]
107pub struct FlexNamer {
108 short_mode: bool,
109 generic_delimiter: Option<(String, String)>,
110}
111impl FlexNamer {
112 #[must_use]
114 pub fn new() -> Self {
115 Default::default()
116 }
117
118 #[must_use]
120 pub fn short_mode(mut self, short_mode: bool) -> Self {
121 self.short_mode = short_mode;
122 self
123 }
124
125 #[must_use]
127 pub fn generic_delimiter(mut self, open: impl Into<String>, close: impl Into<String>) -> Self {
128 self.generic_delimiter = Some((open.into(), close.into()));
129 self
130 }
131}
132impl Namer for FlexNamer {
133 fn assign_name(&self, type_id: TypeId, type_name: &'static str, rule: NameRule) -> String {
134 let name = match rule {
135 NameRule::Auto => {
136 let mut base = if self.short_mode {
137 let re = Regex::new(r"([^<>]*::)+").expect("Invalid regex");
138 re.replace_all(type_name, "").to_string()
139 } else {
140 type_name.replace("::", ".")
141 };
142 if let Some((open, close)) = &self.generic_delimiter {
143 base = base.replace('<', open).replace('>', close);
144 }
145 let mut name = base.clone();
146 let mut count = 1;
147 while let Some(exist_id) = type_info_by_name(&name).map(|t| t.0) {
148 if exist_id != type_id {
149 count += 1;
150 name = format!("{base}{count}");
151 } else {
152 break;
153 }
154 }
155 name
156 }
157 NameRule::Force(force_name) => {
158 let mut base = if self.short_mode {
159 let re = Regex::new(r"([^<>]*::)+").expect("Invalid regex");
160 re.replace_all(type_name, "").to_string()
161 } else {
162 format! {"{}{}", force_name, type_generic_part(type_name).replace("::", ".")}
163 };
164 if let Some((open, close)) = &self.generic_delimiter {
165 base = base.replace('<', open).replace('>', close);
166 }
167 let mut name = base.clone();
168 let mut count = 1;
169 while let Some((exist_id, exist_name)) = type_info_by_name(&name) {
170 if exist_id != type_id {
171 count += 1;
172 tracing::error!("Duplicate name for types: {}, {}", exist_name, type_name);
173 name = format!("{base}{count}");
174 } else {
175 break;
176 }
177 }
178 name
179 }
180 };
181 set_name_type_info(name.clone(), type_id, type_name);
182 name
183 }
184}
185
186mod tests {
187 #[test]
188 fn test_name() {
189 use super::*;
190
191 struct MyString;
192 mod nest {
193 pub(crate) struct MyString;
194 }
195
196 let name = assign_name::<String>(NameRule::Auto);
197 assert_eq!(name, "alloc.string.String");
198 let name = assign_name::<Vec<String>>(NameRule::Auto);
199 assert_eq!(name, "alloc.vec.Vec<alloc.string.String>");
200
201 let name = assign_name::<MyString>(NameRule::Auto);
202 assert_eq!(name, "salvo_oapi.naming.tests.test_name.MyString");
203 let name = assign_name::<nest::MyString>(NameRule::Auto);
204 assert_eq!(name, "salvo_oapi.naming.tests.test_name.nest.MyString");
205
206 }
235}