1use std::fmt::{Debug, Display};
13use std::hash::Hash;
14use std::marker::PhantomData;
15
16use serde::{Deserialize, Serialize};
17
18#[derive(Clone, Deserialize, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
27#[non_exhaustive] pub enum TaglessMemberId {
29 #[cfg(any(
31 feature = "deploy",
32 feature = "deploy_integration",
33 feature = "sim",
34 feature = "sim_runtime",
35 feature = "embedded_runtime"
36 ))]
37 #[cfg_attr(
38 docsrs,
39 doc(cfg(any(
40 feature = "deploy",
41 feature = "deploy_integration",
42 feature = "sim",
43 feature = "sim_runtime",
44 feature = "embedded_runtime"
45 )))
46 )]
47 Legacy {
48 raw_id: u32,
50 },
51 #[cfg(any(feature = "docker_runtime", feature = "ecs_runtime"))]
53 #[cfg_attr(
54 docsrs,
55 doc(cfg(any(feature = "docker_runtime", feature = "ecs_runtime")))
56 )]
57 Docker {
58 container_name: String,
60 },
61 #[cfg(feature = "maelstrom_runtime")]
63 #[cfg_attr(docsrs, doc(cfg(feature = "maelstrom_runtime")))]
64 Maelstrom {
65 node_id: String,
67 },
68}
69
70macro_rules! assert_feature {
71 (#[cfg($meta:meta)] $( $code:stmt )+) => {
72 #[cfg(not($meta))]
73 panic!("Feature {:?} is not enabled.", stringify!($meta));
74
75 #[cfg($meta)]
76 {
77 $( $code )+
78 }
79 };
80}
81
82impl TaglessMemberId {
83 pub fn from_raw_id(_raw_id: u32) -> Self {
88 assert_feature! {
89 #[cfg(any(feature = "deploy", feature = "deploy_integration", feature = "sim", feature = "sim_runtime", feature = "embedded_runtime"))]
90 Self::Legacy { raw_id: _raw_id }
91 }
92 }
93
94 pub fn get_raw_id(&self) -> u32 {
100 assert_feature! {
101 #[cfg(any(feature = "deploy", feature = "deploy_integration", feature = "sim", feature = "sim_runtime", feature = "embedded_runtime"))]
102 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
103 #[allow(
104 irrefutable_let_patterns,
105 reason = "Depends on features."
106 )]
107 let TaglessMemberId::Legacy { raw_id } = self else {
108 panic!("Not `Legacy` variant.");
109 }
110 *raw_id
111 }
112 }
113
114 pub fn from_container_name(_container_name: impl Into<String>) -> Self {
119 assert_feature! {
120 #[cfg(any(feature = "docker_runtime", feature = "ecs_runtime"))]
121 Self::Docker {
122 container_name: _container_name.into(),
123 }
124 }
125 }
126
127 pub fn get_container_name(&self) -> &str {
133 assert_feature! {
134 #[cfg(any(feature = "docker_runtime", feature = "ecs_runtime"))]
135 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
136 #[allow(
137 irrefutable_let_patterns,
138 reason = "Depends on features."
139 )]
140 let TaglessMemberId::Docker { container_name } = self else {
141 panic!("Not `Docker` variant.");
142 }
143 container_name
144 }
145 }
146
147 pub fn from_maelstrom_node_id(_node_id: impl Into<String>) -> Self {
152 assert_feature! {
153 #[cfg(feature = "maelstrom_runtime")]
154 Self::Maelstrom {
155 node_id: _node_id.into(),
156 }
157 }
158 }
159
160 pub fn get_maelstrom_node_id(&self) -> &str {
166 assert_feature! {
167 #[cfg(feature = "maelstrom_runtime")]
168 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
169 #[allow(
170 irrefutable_let_patterns,
171 reason = "Depends on features."
172 )]
173 let TaglessMemberId::Maelstrom { node_id } = self else {
174 panic!("Not `Maelstrom` variant.");
175 }
176 node_id
177 }
178 }
179}
180
181impl Display for TaglessMemberId {
182 fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183 match self {
184 #[cfg(any(
185 feature = "deploy",
186 feature = "deploy_integration",
187 feature = "sim",
188 feature = "sim_runtime",
189 feature = "embedded_runtime"
190 ))]
191 TaglessMemberId::Legacy { raw_id } => write!(_f, "{}", raw_id),
192 #[cfg(any(feature = "docker_runtime", feature = "ecs_runtime"))]
193 TaglessMemberId::Docker { container_name } => write!(_f, "{}", container_name),
194 #[cfg(feature = "maelstrom_runtime")]
195 TaglessMemberId::Maelstrom { node_id } => write!(_f, "{}", node_id),
196 #[expect(
197 clippy::allow_attributes,
198 reason = "Only triggers when `TaglessMemberId` is empty."
199 )]
200 #[allow(
201 unreachable_patterns,
202 reason = "Needed when `TaglessMemberId` is empty."
203 )]
204 _ => panic!(),
205 }
206 }
207}
208
209#[repr(transparent)]
215pub struct MemberId<Tag> {
216 inner: TaglessMemberId,
217 _phantom: PhantomData<Tag>,
218}
219
220impl<Tag> MemberId<Tag> {
221 pub fn into_tagless(self) -> TaglessMemberId {
224 self.inner
225 }
226
227 pub fn from_tagless(inner: TaglessMemberId) -> Self {
229 Self {
230 inner,
231 _phantom: Default::default(),
232 }
233 }
234
235 pub fn from_raw_id(raw_id: u32) -> Self {
240 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
241 #[allow(
242 unreachable_code,
243 reason = "`inner` may be uninhabited depending on features."
244 )]
245 Self {
246 inner: TaglessMemberId::from_raw_id(raw_id),
247 _phantom: Default::default(),
248 }
249 }
250
251 pub fn get_raw_id(&self) -> u32 {
257 self.inner.get_raw_id()
258 }
259}
260
261impl<Tag> Debug for MemberId<Tag> {
262 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263 Display::fmt(self, f)
264 }
265}
266
267impl<Tag> Display for MemberId<Tag> {
268 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
269 write!(
270 f,
271 "MemberId::<{}>({})",
272 std::any::type_name::<Tag>(),
273 self.inner
274 )
275 }
276}
277
278impl<Tag> Clone for MemberId<Tag> {
279 fn clone(&self) -> Self {
280 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
281 #[allow(
282 unreachable_code,
283 reason = "`inner` may be uninhabited depending on features."
284 )]
285 Self {
286 inner: self.inner.clone(),
287 _phantom: Default::default(),
288 }
289 }
290}
291
292impl<Tag> Serialize for MemberId<Tag> {
293 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
294 where
295 S: serde::Serializer,
296 {
297 self.inner.serialize(serializer)
298 }
299}
300
301impl<'a, Tag> Deserialize<'a> for MemberId<Tag> {
302 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
303 where
304 D: serde::Deserializer<'a>,
305 {
306 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
307 #[allow(
308 unreachable_code,
309 reason = "`inner` may be uninhabited depending on features."
310 )]
311 Ok(Self::from_tagless(TaglessMemberId::deserialize(
312 deserializer,
313 )?))
314 }
315}
316
317impl<Tag> PartialOrd for MemberId<Tag> {
318 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
319 Some(self.cmp(other))
320 }
321}
322
323impl<Tag> Ord for MemberId<Tag> {
324 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
325 self.inner.cmp(&other.inner)
326 }
327}
328
329impl<Tag> PartialEq for MemberId<Tag> {
330 fn eq(&self, other: &Self) -> bool {
331 self.inner == other.inner
332 }
333}
334
335impl<Tag> Eq for MemberId<Tag> {}
336
337impl<Tag> Hash for MemberId<Tag> {
338 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
339 self.inner.hash(state);
340 std::any::type_name::<Tag>().hash(state);
343 }
344}