1#[cfg(not(feature = "std"))]
38extern crate alloc;
39
40#[cfg(not(feature = "std"))]
41use alloc::sync::Arc;
42
43#[cfg(feature = "std")]
44use std::sync::Arc;
45
46#[derive(Clone)]
74pub struct RecordKey(RecordKeyInner);
75
76impl core::fmt::Debug for RecordKey {
78 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
79 match &self.0 {
80 RecordKeyInner::Static(s) => f.debug_tuple("RecordKey::Static").field(s).finish(),
81 RecordKeyInner::Dynamic(s) => f.debug_tuple("RecordKey::Dynamic").field(s).finish(),
82 }
83 }
84}
85
86#[derive(Clone)]
87enum RecordKeyInner {
88 Static(&'static str),
90 Dynamic(Arc<str>),
92}
93
94impl RecordKey {
95 #[inline]
99 #[must_use]
100 pub const fn new(s: &'static str) -> Self {
101 Self(RecordKeyInner::Static(s))
102 }
103
104 #[inline]
108 #[must_use]
109 pub fn dynamic(s: impl Into<Arc<str>>) -> Self {
110 Self(RecordKeyInner::Dynamic(s.into()))
111 }
112
113 #[inline]
117 #[must_use]
118 pub fn from_dynamic(s: &str) -> Self {
119 Self(RecordKeyInner::Dynamic(Arc::from(s)))
120 }
121
122 #[inline]
124 pub fn as_str(&self) -> &str {
125 match &self.0 {
126 RecordKeyInner::Static(s) => s,
127 RecordKeyInner::Dynamic(s) => s,
128 }
129 }
130
131 #[inline]
133 pub fn is_static(&self) -> bool {
134 matches!(self.0, RecordKeyInner::Static(_))
135 }
136}
137
138impl core::hash::Hash for RecordKey {
141 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
142 self.as_str().hash(state);
144 }
145}
146
147impl PartialEq for RecordKey {
148 fn eq(&self, other: &Self) -> bool {
149 self.as_str() == other.as_str()
150 }
151}
152
153impl Eq for RecordKey {}
154
155impl PartialEq<str> for RecordKey {
157 fn eq(&self, other: &str) -> bool {
158 self.as_str() == other
159 }
160}
161
162impl PartialEq<&str> for RecordKey {
164 fn eq(&self, other: &&str) -> bool {
165 self.as_str() == *other
166 }
167}
168
169impl PartialOrd for RecordKey {
170 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
171 Some(self.cmp(other))
172 }
173}
174
175impl Ord for RecordKey {
176 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
177 self.as_str().cmp(other.as_str())
178 }
179}
180
181impl From<&'static str> for RecordKey {
183 #[inline]
184 fn from(s: &'static str) -> Self {
185 Self::new(s)
186 }
187}
188
189#[cfg(all(feature = "alloc", not(feature = "std")))]
191impl From<alloc::string::String> for RecordKey {
192 #[inline]
193 fn from(s: alloc::string::String) -> Self {
194 Self::dynamic(s)
195 }
196}
197
198#[cfg(feature = "std")]
200impl From<String> for RecordKey {
201 #[inline]
202 fn from(s: String) -> Self {
203 Self::dynamic(s)
204 }
205}
206
207impl core::fmt::Display for RecordKey {
208 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
209 f.write_str(self.as_str())
210 }
211}
212
213impl AsRef<str> for RecordKey {
214 fn as_ref(&self) -> &str {
215 self.as_str()
216 }
217}
218
219impl core::borrow::Borrow<str> for RecordKey {
223 fn borrow(&self) -> &str {
224 self.as_str()
225 }
226}
227
228#[cfg(feature = "std")]
231impl serde::Serialize for RecordKey {
232 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
233 serializer.serialize_str(self.as_str())
234 }
235}
236
237#[cfg(feature = "std")]
238impl<'de> serde::Deserialize<'de> for RecordKey {
239 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
240 let s = String::deserialize(deserializer)?;
241 Ok(Self::dynamic(s))
242 }
243}
244
245#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
258pub struct RecordId(pub(crate) u32);
259
260impl RecordId {
261 #[inline]
266 pub const fn new(index: u32) -> Self {
267 Self(index)
268 }
269
270 #[inline]
272 pub const fn index(self) -> usize {
273 self.0 as usize
274 }
275
276 #[inline]
278 pub const fn raw(self) -> u32 {
279 self.0
280 }
281}
282
283impl core::fmt::Display for RecordId {
284 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
285 write!(f, "RecordId({})", self.0)
286 }
287}
288
289#[cfg(feature = "std")]
290impl serde::Serialize for RecordId {
291 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
292 serializer.serialize_u32(self.0)
293 }
294}
295
296#[cfg(feature = "std")]
297impl<'de> serde::Deserialize<'de> for RecordId {
298 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
299 let index = u32::deserialize(deserializer)?;
300 Ok(Self(index))
301 }
302}
303
304#[cfg(all(test, feature = "std"))]
307mod tests {
308 use super::*;
309
310 #[test]
311 fn test_record_key_static() {
312 let key: RecordKey = "sensors.temperature".into();
313 assert!(key.is_static());
314 assert_eq!(key.as_str(), "sensors.temperature");
315 }
316
317 #[test]
318 fn test_record_key_dynamic() {
319 let key = RecordKey::dynamic("sensors.temperature".to_string());
320 assert!(!key.is_static());
321 assert_eq!(key.as_str(), "sensors.temperature");
322 }
323
324 #[test]
325 fn test_record_key_equality() {
326 let static_key: RecordKey = "sensors.temp".into();
327 let dynamic_key = RecordKey::dynamic("sensors.temp".to_string());
328
329 assert_eq!(static_key, dynamic_key);
331 }
332
333 #[test]
334 fn test_record_key_hash_consistency() {
335 use core::hash::{Hash, Hasher};
336 use std::collections::hash_map::DefaultHasher;
337
338 fn hash_key(key: &RecordKey) -> u64 {
339 let mut hasher = DefaultHasher::new();
340 key.hash(&mut hasher);
341 hasher.finish()
342 }
343
344 let static_key: RecordKey = "sensors.temp".into();
345 let dynamic_key = RecordKey::dynamic("sensors.temp".to_string());
346
347 assert_eq!(hash_key(&static_key), hash_key(&dynamic_key));
349 }
350
351 #[test]
352 fn test_record_key_borrow() {
353 use std::collections::HashMap;
354
355 let mut map: HashMap<RecordKey, i32> = HashMap::new();
356 map.insert("sensors.temp".into(), 42);
357
358 assert_eq!(map.get("sensors.temp"), Some(&42));
360 }
361
362 #[test]
363 fn test_record_id_basic() {
364 let id = RecordId::new(42);
365 assert_eq!(id.index(), 42);
366 assert_eq!(id.raw(), 42);
367 }
368
369 #[test]
370 fn test_record_id_copy() {
371 let id1 = RecordId::new(10);
372 let id2 = id1; assert_eq!(id1, id2);
374 }
375
376 #[test]
377 fn test_record_key_display() {
378 let key: RecordKey = "sensors.temperature".into();
379 assert_eq!(format!("{}", key), "sensors.temperature");
380 }
381
382 #[test]
383 fn test_record_key_debug() {
384 let static_key: RecordKey = "sensors.temp".into();
385 let dynamic_key = RecordKey::dynamic("sensors.temp".to_string());
386
387 let static_debug = format!("{:?}", static_key);
389 let dynamic_debug = format!("{:?}", dynamic_key);
390
391 assert!(static_debug.contains("Static"));
392 assert!(dynamic_debug.contains("Dynamic"));
393 }
394
395 #[test]
396 fn test_record_key_partial_eq_str() {
397 let key: RecordKey = "sensors.temperature".into();
398
399 assert!(key == "sensors.temperature");
401 assert!(key != "other.key");
402
403 let s: &str = "sensors.temperature";
405 assert!(key == s);
406 }
407
408 #[test]
409 fn test_record_key_from_string() {
410 let owned = "sensors.temperature".to_string();
411 let key: RecordKey = owned.into();
412
413 assert!(!key.is_static());
414 assert_eq!(key.as_str(), "sensors.temperature");
415 }
416
417 #[test]
418 fn test_record_id_display() {
419 let id = RecordId::new(42);
420 assert_eq!(format!("{}", id), "RecordId(42)");
421 }
422}