zng_unique_id/
named.rs
1use std::{
2 fmt,
3 hash::{BuildHasher, Hash},
4};
5
6use std::collections::hash_map;
7
8use crate::{IdEntry, IdMap};
9
10struct ConstDefaultHashBuilder;
11impl BuildHasher for ConstDefaultHashBuilder {
12 type Hasher = std::collections::hash_map::DefaultHasher;
13
14 fn build_hasher(&self) -> Self::Hasher {
15 std::collections::hash_map::DefaultHasher::default()
16 }
17}
18
19type DefaultHashMap<K, V> = std::collections::HashMap<K, V, ConstDefaultHashBuilder>;
20
21const fn default_hash_map_new<K, V>() -> DefaultHashMap<K, V> {
22 DefaultHashMap::with_hasher(ConstDefaultHashBuilder)
23}
24
25#[doc(hidden)]
26pub use zng_txt::Txt;
27
28struct NameIdMap<I> {
30 name_to_id: DefaultHashMap<Txt, I>,
31 id_to_name: IdMap<I, Txt>,
32}
33impl<I> NameIdMap<I>
34where
35 I: Copy + PartialEq + Eq + Hash + fmt::Debug,
36{
37 pub const fn new() -> Self {
38 NameIdMap {
39 name_to_id: default_hash_map_new(),
40 id_to_name: IdMap::new(),
41 }
42 }
43
44 pub fn set(&mut self, name: Txt, id: I) -> Result<(), IdNameError<I>> {
45 if name.is_empty() {
46 return Ok(());
47 }
48
49 match self.id_to_name.entry(id) {
50 IdEntry::Occupied(e) => {
51 if *e.get() == name {
52 Ok(())
53 } else {
54 Err(IdNameError::AlreadyNamed(e.get().clone()))
55 }
56 }
57 IdEntry::Vacant(e) => match self.name_to_id.entry(name.clone()) {
58 hash_map::Entry::Occupied(ne) => Err(IdNameError::NameUsed(*ne.get())),
59 hash_map::Entry::Vacant(ne) => {
60 e.insert(name);
61 ne.insert(id);
62 Ok(())
63 }
64 },
65 }
66 }
67
68 pub fn get_id_or_insert(&mut self, name: Txt, new_unique: impl FnOnce() -> I) -> I {
69 if name.is_empty() {
70 return new_unique();
71 }
72 match self.name_to_id.entry(name.clone()) {
73 hash_map::Entry::Occupied(e) => *e.get(),
74 hash_map::Entry::Vacant(e) => {
75 let id = new_unique();
76 e.insert(id);
77 self.id_to_name.insert(id, name);
78 id
79 }
80 }
81 }
82
83 pub fn new_named(&mut self, name: Txt, new_unique: impl FnOnce() -> I) -> Result<I, IdNameError<I>> {
84 if name.is_empty() {
85 Ok(new_unique())
86 } else {
87 match self.name_to_id.entry(name.clone()) {
88 hash_map::Entry::Occupied(e) => Err(IdNameError::NameUsed(*e.get())),
89 hash_map::Entry::Vacant(e) => {
90 let id = new_unique();
91 e.insert(id);
92 self.id_to_name.insert(id, name);
93 Ok(id)
94 }
95 }
96 }
97 }
98
99 pub fn get_name(&self, id: I) -> Txt {
100 self.id_to_name.get(&id).cloned().unwrap_or_default()
101 }
102}
103
104#[derive(Clone, Debug)]
106pub enum IdNameError<I: Clone + Copy + fmt::Debug> {
107 AlreadyNamed(Txt),
111 NameUsed(I),
115}
116impl<I: Clone + Copy + fmt::Debug> fmt::Display for IdNameError<I> {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 match self {
119 IdNameError::AlreadyNamed(name) => write!(f, "cannot name the id, it is already called `{name:?}`"),
120 IdNameError::NameUsed(id) => write!(f, "cannot name the id, it is already the name of {id:#?}"),
121 }
122 }
123}
124impl<I: Clone + Copy + fmt::Debug> std::error::Error for IdNameError<I> {}
125
126#[doc(hidden)]
127pub struct UniqueIdNameStore<I>(parking_lot::RwLock<NameIdMap<I>>);
128impl<I> UniqueIdNameStore<I>
129where
130 I: Copy + PartialEq + Eq + Hash + fmt::Debug,
131{
132 pub const fn new() -> Self {
133 Self(parking_lot::const_rwlock(NameIdMap::new()))
134 }
135
136 pub fn named(&self, name: impl Into<Txt>, new_unique: impl FnOnce() -> I) -> I {
137 self.0.write().get_id_or_insert(name.into(), new_unique)
138 }
139
140 pub fn named_new(&self, name: impl Into<Txt>, new_unique: impl FnOnce() -> I) -> Result<I, IdNameError<I>> {
141 self.0.write().new_named(name.into(), new_unique)
142 }
143
144 pub fn name(&self, id: I) -> Txt {
145 self.0.read().get_name(id)
146 }
147
148 pub fn set_name(&self, name: impl Into<Txt>, id: I) -> Result<(), IdNameError<I>> {
149 self.0.write().set(name.into(), id)
150 }
151}
152impl<I> Default for UniqueIdNameStore<I>
153where
154 I: Copy + PartialEq + Eq + Hash + fmt::Debug,
155{
156 fn default() -> Self {
157 Self::new()
158 }
159}
160
161#[macro_export]
163macro_rules! impl_unique_id_name {
164 ($UniqueId:ident) => {
165 $crate::paste! {
166 $crate::hot_static! {
167 static [<$UniqueId:upper _ID_NAMES>]: $crate::UniqueIdNameStore<$UniqueId> = $crate::UniqueIdNameStore::new();
168 }
169 }
170
171 impl $UniqueId {
172 fn names_store() -> &'static $crate::UniqueIdNameStore<Self> {
173 $crate::paste! {
174 $crate::hot_static_ref! {
175 [<$UniqueId:upper _ID_NAMES>]
176 }
177 }
178 }
179
180 pub fn named(name: impl Into<$crate::Txt>) -> Self {
186 Self::names_store().named(name, Self::new_unique)
187 }
188
189 pub fn debug_named(name: impl Into<$crate::Txt>) -> Self {
194 #[cfg(debug_assertions)]
195 return Self::named(name);
196
197 #[cfg(not(debug_assertions))]
198 {
199 let _ = name;
200 Self::new_unique()
201 }
202 }
203
204 pub fn named_new(name: impl Into<$crate::Txt>) -> Result<Self, $crate::IdNameError<Self>> {
209 Self::names_store().named_new(name.into(), Self::new_unique)
210 }
211
212 pub fn name(self) -> $crate::Txt {
214 Self::names_store().name(self)
215 }
216
217 pub fn set_name(self, name: impl Into<$crate::Txt>) -> Result<(), $crate::IdNameError<Self>> {
223 Self::names_store().set_name(name.into(), self)
224 }
225 }
226 };
227}
228
229#[macro_export]
231macro_rules! impl_unique_id_fmt {
232 ($UniqueId:ident) => {
233 impl std::fmt::Debug for $UniqueId {
234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235 let name = self.name();
236 if f.alternate() {
237 f.debug_struct(stringify!($UniqueId))
238 .field("id", &self.get())
239 .field("sequential", &self.sequential())
240 .field("name", &name)
241 .finish()
242 } else if !name.is_empty() {
243 write!(f, r#"{}("{name}")"#, stringify!($UniqueId))
244 } else {
245 write!(f, "{}({})", stringify!($UniqueId), self.sequential())
246 }
247 }
248 }
249 impl std::fmt::Display for $UniqueId {
250 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251 let name = self.name();
252 if !name.is_empty() {
253 write!(f, "{name}")
254 } else if f.alternate() {
255 write!(f, "#{}", self.sequential())
256 } else {
257 write!(f, "{}({})", stringify!($UniqueId), self.sequential())
258 }
259 }
260 }
261 };
262}