1use crate::{atomics::AtomicU64, cow::Cow, IntoLabels, KeyHasher, Label, SharedString};
2use std::{
3 borrow::Borrow,
4 cmp, fmt,
5 hash::{Hash, Hasher},
6 slice::Iter,
7 sync::atomic::{AtomicBool, Ordering},
8};
9
10const NO_LABELS: [Label; 0] = [];
11
12#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
14pub struct KeyName(SharedString);
15
16impl KeyName {
17 pub const fn from_const_str(name: &'static str) -> Self {
19 KeyName(SharedString::const_str(name))
20 }
21
22 pub fn as_str(&self) -> &str {
24 &self.0
25 }
26}
27
28impl<T> From<T> for KeyName
29where
30 T: Into<SharedString>,
31{
32 fn from(name: T) -> Self {
33 KeyName(name.into())
34 }
35}
36
37impl Borrow<str> for KeyName {
38 fn borrow(&self) -> &str {
39 self.0.borrow()
40 }
41}
42
43#[derive(Debug)]
60pub struct Key {
61 name: KeyName,
62 labels: Cow<'static, [Label]>,
63 hashed: AtomicBool,
64 hash: AtomicU64,
65}
66
67impl Key {
68 pub fn from_name<N>(name: N) -> Self
70 where
71 N: Into<KeyName>,
72 {
73 let name = name.into();
74 let labels = Cow::from_owned(Vec::new());
75
76 Self::builder(name, labels)
77 }
78
79 pub fn from_parts<N, L>(name: N, labels: L) -> Self
81 where
82 N: Into<KeyName>,
83 L: IntoLabels,
84 {
85 let name = name.into();
86 let labels = Cow::from_owned(labels.into_labels());
87
88 Self::builder(name, labels)
89 }
90
91 pub fn from_static_labels<N>(name: N, labels: &'static [Label]) -> Self
93 where
94 N: Into<KeyName>,
95 {
96 Self {
97 name: name.into(),
98 labels: Cow::const_slice(labels),
99 hashed: AtomicBool::new(false),
100 hash: AtomicU64::new(0),
101 }
102 }
103
104 pub const fn from_static_name(name: &'static str) -> Self {
108 Self::from_static_parts(name, &NO_LABELS)
109 }
110
111 pub const fn from_static_parts(name: &'static str, labels: &'static [Label]) -> Self {
115 Self {
116 name: KeyName::from_const_str(name),
117 labels: Cow::const_slice(labels),
118 hashed: AtomicBool::new(false),
119 hash: AtomicU64::new(0),
120 }
121 }
122
123 fn builder(name: KeyName, labels: Cow<'static, [Label]>) -> Self {
124 let hash = generate_key_hash(&name, &labels);
125
126 Self { name, labels, hashed: AtomicBool::new(true), hash: AtomicU64::new(hash) }
127 }
128
129 pub fn name(&self) -> &str {
131 self.name.0.as_ref()
132 }
133
134 pub fn labels(&self) -> Iter<Label> {
136 self.labels.iter()
137 }
138
139 pub fn into_parts(self) -> (KeyName, Vec<Label>) {
141 (self.name, self.labels.into_owned())
142 }
143
144 pub fn with_extra_labels(&self, extra_labels: Vec<Label>) -> Self {
146 if extra_labels.is_empty() {
147 return self.clone();
148 }
149
150 let name = self.name.clone();
151 let mut labels = self.labels.clone().into_owned();
152 labels.extend(extra_labels);
153
154 Self::builder(name, labels.into())
155 }
156
157 pub fn get_hash(&self) -> u64 {
159 if self.hashed.load(Ordering::Acquire) {
160 self.hash.load(Ordering::Acquire)
161 } else {
162 let hash = generate_key_hash(&self.name, &self.labels);
163 self.hash.store(hash, Ordering::Release);
164 self.hashed.store(true, Ordering::Release);
165 hash
166 }
167 }
168}
169
170fn generate_key_hash(name: &KeyName, labels: &Cow<'static, [Label]>) -> u64 {
171 let mut hasher = KeyHasher::default();
172 key_hasher_impl(&mut hasher, name, labels);
173 hasher.finish()
174}
175
176fn key_hasher_impl<H: Hasher>(state: &mut H, name: &KeyName, labels: &Cow<'static, [Label]>) {
177 name.0.hash(state);
178 labels.hash(state);
179}
180
181impl Clone for Key {
182 fn clone(&self) -> Self {
183 Self {
184 name: self.name.clone(),
185 labels: self.labels.clone(),
186 hashed: AtomicBool::new(self.hashed.load(Ordering::Acquire)),
187 hash: AtomicU64::new(self.hash.load(Ordering::Acquire)),
188 }
189 }
190}
191
192impl PartialEq for Key {
193 fn eq(&self, other: &Self) -> bool {
194 self.name == other.name && self.labels == other.labels
195 }
196}
197
198impl Eq for Key {}
199
200impl PartialOrd for Key {
201 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
202 Some(self.cmp(other))
203 }
204}
205
206impl Ord for Key {
207 fn cmp(&self, other: &Self) -> cmp::Ordering {
208 (&self.name, &self.labels).cmp(&(&other.name, &other.labels))
209 }
210}
211
212impl Hash for Key {
213 fn hash<H: Hasher>(&self, state: &mut H) {
214 key_hasher_impl(state, &self.name, &self.labels);
215 }
216}
217
218impl fmt::Display for Key {
219 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
220 if self.labels.is_empty() {
221 write!(f, "Key({})", self.name.as_str())
222 } else {
223 write!(f, "Key({}, [", self.name.as_str())?;
224 let mut first = true;
225 for label in self.labels.as_ref() {
226 if first {
227 write!(f, "{} = {}", label.0, label.1)?;
228 first = false;
229 } else {
230 write!(f, ", {} = {}", label.0, label.1)?;
231 }
232 }
233 write!(f, "])")
234 }
235 }
236}
237
238impl<T> From<T> for Key
239where
240 T: Into<KeyName>,
241{
242 fn from(name: T) -> Self {
243 Self::from_name(name)
244 }
245}
246
247impl<N, L> From<(N, L)> for Key
248where
249 N: Into<KeyName>,
250 L: IntoLabels,
251{
252 fn from(parts: (N, L)) -> Self {
253 Self::from_parts(parts.0, parts.1)
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::Key;
260 use crate::{KeyName, Label};
261 use std::{collections::HashMap, ops::Deref, sync::Arc};
262
263 static BORROWED_NAME: &str = "name";
264 static FOOBAR_NAME: &str = "foobar";
265 static BORROWED_BASIC: Key = Key::from_static_name(BORROWED_NAME);
266 static LABELS: [Label; 1] = [Label::from_static_parts("key", "value")];
267 static BORROWED_LABELS: Key = Key::from_static_parts(BORROWED_NAME, &LABELS);
268
269 #[test]
270 fn test_key_ord_and_partialord() {
271 let keys_expected: Vec<Key> =
272 vec![Key::from_name("aaaa"), Key::from_name("bbbb"), Key::from_name("cccc")];
273
274 let keys_unsorted: Vec<Key> =
275 vec![Key::from_name("bbbb"), Key::from_name("cccc"), Key::from_name("aaaa")];
276
277 let keys = {
278 let mut keys = keys_unsorted.clone();
279 keys.sort();
280 keys
281 };
282 assert_eq!(keys, keys_expected);
283
284 let keys = {
285 let mut keys = keys_unsorted.clone();
286 keys.sort_by(|a, b| a.partial_cmp(b).unwrap());
287 keys
288 };
289 assert_eq!(keys, keys_expected);
290 }
291
292 #[test]
293 fn test_key_eq_and_hash() {
294 let mut keys = HashMap::new();
295
296 let owned_basic: Key = Key::from_name("name");
297 assert_eq!(&owned_basic, &BORROWED_BASIC);
298
299 let previous = keys.insert(owned_basic, 42);
300 assert!(previous.is_none());
301
302 let previous = keys.get(&BORROWED_BASIC);
303 assert_eq!(previous, Some(&42));
304
305 let labels = LABELS.to_vec();
306 let owned_labels = Key::from_parts(BORROWED_NAME, labels);
307 assert_eq!(&owned_labels, &BORROWED_LABELS);
308
309 let previous = keys.insert(owned_labels, 43);
310 assert!(previous.is_none());
311
312 let previous = keys.get(&BORROWED_LABELS);
313 assert_eq!(previous, Some(&43));
314
315 let basic: Key = "constant_key".into();
316 let cloned_basic = basic.clone();
317 assert_eq!(basic, cloned_basic);
318 }
319
320 #[test]
321 fn test_key_data_proper_display() {
322 let key1 = Key::from_name("foobar");
323 let result1 = key1.to_string();
324 assert_eq!(result1, "Key(foobar)");
325
326 let key2 = Key::from_parts(FOOBAR_NAME, vec![Label::new("system", "http")]);
327 let result2 = key2.to_string();
328 assert_eq!(result2, "Key(foobar, [system = http])");
329
330 let key3 = Key::from_parts(
331 FOOBAR_NAME,
332 vec![Label::new("system", "http"), Label::new("user", "joe")],
333 );
334 let result3 = key3.to_string();
335 assert_eq!(result3, "Key(foobar, [system = http, user = joe])");
336
337 let key4 = Key::from_parts(
338 FOOBAR_NAME,
339 vec![
340 Label::new("black", "black"),
341 Label::new("lives", "lives"),
342 Label::new("matter", "matter"),
343 ],
344 );
345 let result4 = key4.to_string();
346 assert_eq!(result4, "Key(foobar, [black = black, lives = lives, matter = matter])");
347 }
348
349 #[test]
350 fn test_key_name_equality() {
351 static KEY_NAME: &str = "key_name";
352
353 let borrowed_const = KeyName::from_const_str(KEY_NAME);
354 let borrowed_nonconst = KeyName::from(KEY_NAME);
355 let owned = KeyName::from(KEY_NAME.to_owned());
356
357 let shared_arc = Arc::from(KEY_NAME);
358 let shared = KeyName::from(Arc::clone(&shared_arc));
359
360 assert_eq!(borrowed_const, borrowed_nonconst);
361 assert_eq!(borrowed_const.as_str(), borrowed_nonconst.as_str());
362 assert_eq!(borrowed_const, owned);
363 assert_eq!(borrowed_const.as_str(), owned.as_str());
364 assert_eq!(borrowed_const, shared);
365 assert_eq!(borrowed_const.as_str(), shared.as_str());
366 }
367
368 #[test]
369 fn test_shared_key_name_drop_logic() {
370 let shared_arc = Arc::from("foo");
371 let shared = KeyName::from(Arc::clone(&shared_arc));
372
373 assert_eq!(shared_arc.deref(), shared.as_str());
374
375 assert_eq!(Arc::strong_count(&shared_arc), 2);
376 drop(shared);
377 assert_eq!(Arc::strong_count(&shared_arc), 1);
378
379 let shared_weak = Arc::downgrade(&shared_arc);
380 assert_eq!(Arc::strong_count(&shared_arc), 1);
381
382 let shared = KeyName::from(Arc::clone(&shared_arc));
383 assert_eq!(shared_arc.deref(), shared.as_str());
384 assert_eq!(Arc::strong_count(&shared_arc), 2);
385
386 drop(shared_arc);
387 assert_eq!(shared_weak.strong_count(), 1);
388
389 drop(shared);
390 assert_eq!(shared_weak.strong_count(), 0);
391 }
392}