1use std::sync::atomic::{AtomicU64, Ordering};
9
10use serde::{Deserialize, Serialize};
11
12macro_rules! u64_newtype {
13 ($(#[$m:meta])* $name:ident) => {
14 $(#[$m])*
15 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
16 pub struct $name(pub u64);
17
18 impl $name {
19 #[inline]
21 pub const fn value(self) -> u64 {
22 self.0
23 }
24 }
25
26 impl From<u64> for $name {
27 #[inline]
28 fn from(v: u64) -> Self {
29 $name(v)
30 }
31 }
32
33 impl core::fmt::Display for $name {
34 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
35 write!(f, "{}#{}", stringify!($name), self.0)
36 }
37 }
38 };
39}
40
41u64_newtype!(
42 FrameId
44);
45u64_newtype!(
46 SessionId
48);
49u64_newtype!(
50 WindowId
52);
53u64_newtype!(
54 EventId
56);
57
58#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
60pub struct SourceId(pub String);
61
62impl SourceId {
63 pub fn new(s: impl Into<String>) -> Self {
65 SourceId(s.into())
66 }
67
68 pub fn as_str(&self) -> &str {
70 &self.0
71 }
72}
73
74impl From<&str> for SourceId {
75 fn from(s: &str) -> Self {
76 SourceId(s.to_string())
77 }
78}
79
80impl From<String> for SourceId {
81 fn from(s: String) -> Self {
82 SourceId(s)
83 }
84}
85
86impl core::fmt::Display for SourceId {
87 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
88 f.write_str(&self.0)
89 }
90}
91
92#[derive(Debug, Default)]
98pub struct IdGenerator {
99 frame: AtomicU64,
100 window: AtomicU64,
101 event: AtomicU64,
102 session: AtomicU64,
103}
104
105impl IdGenerator {
106 pub const fn new() -> Self {
108 IdGenerator {
109 frame: AtomicU64::new(0),
110 window: AtomicU64::new(0),
111 event: AtomicU64::new(0),
112 session: AtomicU64::new(0),
113 }
114 }
115
116 pub fn next_frame(&self) -> FrameId {
118 FrameId(self.frame.fetch_add(1, Ordering::Relaxed))
119 }
120
121 pub fn next_window(&self) -> WindowId {
123 WindowId(self.window.fetch_add(1, Ordering::Relaxed))
124 }
125
126 pub fn next_event(&self) -> EventId {
128 EventId(self.event.fetch_add(1, Ordering::Relaxed))
129 }
130
131 pub fn next_session(&self) -> SessionId {
133 SessionId(self.session.fetch_add(1, Ordering::Relaxed))
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn id_generator_is_monotonic_and_independent() {
143 let g = IdGenerator::new();
144 assert_eq!(g.next_frame(), FrameId(0));
145 assert_eq!(g.next_frame(), FrameId(1));
146 assert_eq!(g.next_window(), WindowId(0));
147 assert_eq!(g.next_event(), EventId(0));
148 assert_eq!(g.next_frame(), FrameId(2));
149 assert_eq!(g.next_session(), SessionId(0));
150 }
151
152 #[test]
153 fn source_id_roundtrips_and_displays() {
154 let s = SourceId::from("esp32-com7");
155 assert_eq!(s.as_str(), "esp32-com7");
156 assert_eq!(s.to_string(), "esp32-com7");
157 let json = serde_json::to_string(&s).unwrap();
158 assert_eq!(serde_json::from_str::<SourceId>(&json).unwrap(), s);
159 }
160
161 #[test]
162 fn u64_newtype_display_and_serde() {
163 let f = FrameId(42);
164 assert_eq!(f.value(), 42);
165 assert_eq!(f.to_string(), "FrameId#42");
166 let json = serde_json::to_string(&f).unwrap();
167 assert_eq!(json, "42");
168 assert_eq!(serde_json::from_str::<FrameId>(&json).unwrap(), f);
169 }
170}