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(feature = "docker_runtime")]
53 #[cfg_attr(docsrs, doc(cfg(feature = "docker_runtime")))]
54 Docker {
55 container_name: String,
57 },
58 #[cfg(feature = "maelstrom_runtime")]
60 #[cfg_attr(docsrs, doc(cfg(feature = "maelstrom_runtime")))]
61 Maelstrom {
62 node_id: String,
64 },
65}
66
67macro_rules! assert_feature {
68 (#[cfg($meta:meta)] $( $code:stmt )+) => {
69 #[cfg(not($meta))]
70 panic!("Feature {:?} is not enabled.", stringify!($meta));
71
72 #[cfg($meta)]
73 {
74 $( $code )+
75 }
76 };
77}
78
79impl TaglessMemberId {
80 pub fn from_raw_id(_raw_id: u32) -> Self {
85 assert_feature! {
86 #[cfg(any(feature = "deploy", feature = "deploy_integration", feature = "sim", feature = "sim_runtime", feature = "embedded_runtime"))]
87 Self::Legacy { raw_id: _raw_id }
88 }
89 }
90
91 pub fn get_raw_id(&self) -> u32 {
97 assert_feature! {
98 #[cfg(any(feature = "deploy", feature = "deploy_integration", feature = "sim", feature = "sim_runtime", feature = "embedded_runtime"))]
99 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
100 #[allow(
101 irrefutable_let_patterns,
102 reason = "Depends on features."
103 )]
104 let TaglessMemberId::Legacy { raw_id } = self else {
105 panic!("Not `Legacy` variant.");
106 }
107 *raw_id
108 }
109 }
110
111 pub fn from_container_name(_container_name: impl Into<String>) -> Self {
116 assert_feature! {
117 #[cfg(feature = "docker_runtime")]
118 Self::Docker {
119 container_name: _container_name.into(),
120 }
121 }
122 }
123
124 pub fn get_container_name(&self) -> &str {
130 assert_feature! {
131 #[cfg(feature = "docker_runtime")]
132 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
133 #[allow(
134 irrefutable_let_patterns,
135 reason = "Depends on features."
136 )]
137 let TaglessMemberId::Docker { container_name } = self else {
138 panic!("Not `Docker` variant.");
139 }
140 container_name
141 }
142 }
143
144 pub fn from_maelstrom_node_id(_node_id: impl Into<String>) -> Self {
149 assert_feature! {
150 #[cfg(feature = "maelstrom_runtime")]
151 Self::Maelstrom {
152 node_id: _node_id.into(),
153 }
154 }
155 }
156
157 pub fn get_maelstrom_node_id(&self) -> &str {
163 assert_feature! {
164 #[cfg(feature = "maelstrom_runtime")]
165 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
166 #[allow(
167 irrefutable_let_patterns,
168 reason = "Depends on features."
169 )]
170 let TaglessMemberId::Maelstrom { node_id } = self else {
171 panic!("Not `Maelstrom` variant.");
172 }
173 node_id
174 }
175 }
176}
177
178impl Display for TaglessMemberId {
179 fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 match self {
181 #[cfg(any(
182 feature = "deploy",
183 feature = "deploy_integration",
184 feature = "sim",
185 feature = "sim_runtime",
186 feature = "embedded_runtime"
187 ))]
188 TaglessMemberId::Legacy { raw_id } => write!(_f, "{}", raw_id),
189 #[cfg(feature = "docker_runtime")]
190 TaglessMemberId::Docker { container_name } => write!(_f, "{}", container_name),
191 #[cfg(feature = "maelstrom_runtime")]
192 TaglessMemberId::Maelstrom { node_id } => write!(_f, "{}", node_id),
193 #[expect(
194 clippy::allow_attributes,
195 reason = "Only triggers when `TaglessMemberId` is empty."
196 )]
197 #[allow(
198 unreachable_patterns,
199 reason = "Needed when `TaglessMemberId` is empty."
200 )]
201 _ => panic!(),
202 }
203 }
204}
205
206#[repr(transparent)]
212pub struct MemberId<Tag> {
213 inner: TaglessMemberId,
214 _phantom: PhantomData<Tag>,
215}
216
217impl<Tag> MemberId<Tag> {
218 pub fn into_tagless(self) -> TaglessMemberId {
221 self.inner
222 }
223
224 pub fn from_tagless(inner: TaglessMemberId) -> Self {
226 Self {
227 inner,
228 _phantom: Default::default(),
229 }
230 }
231
232 pub fn from_raw_id(raw_id: u32) -> Self {
237 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
238 #[allow(
239 unreachable_code,
240 reason = "`inner` may be uninhabited depending on features."
241 )]
242 Self {
243 inner: TaglessMemberId::from_raw_id(raw_id),
244 _phantom: Default::default(),
245 }
246 }
247
248 pub fn get_raw_id(&self) -> u32 {
254 self.inner.get_raw_id()
255 }
256}
257
258impl<Tag> Debug for MemberId<Tag> {
259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
260 Display::fmt(self, f)
261 }
262}
263
264impl<Tag> Display for MemberId<Tag> {
265 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
266 write!(
267 f,
268 "MemberId::<{}>({})",
269 std::any::type_name::<Tag>(),
270 self.inner
271 )
272 }
273}
274
275impl<Tag> Clone for MemberId<Tag> {
276 fn clone(&self) -> Self {
277 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
278 #[allow(
279 unreachable_code,
280 reason = "`inner` may be uninhabited depending on features."
281 )]
282 Self {
283 inner: self.inner.clone(),
284 _phantom: Default::default(),
285 }
286 }
287}
288
289impl<Tag> Serialize for MemberId<Tag> {
290 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
291 where
292 S: serde::Serializer,
293 {
294 self.inner.serialize(serializer)
295 }
296}
297
298impl<'a, Tag> Deserialize<'a> for MemberId<Tag> {
299 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
300 where
301 D: serde::Deserializer<'a>,
302 {
303 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
304 #[allow(
305 unreachable_code,
306 reason = "`inner` may be uninhabited depending on features."
307 )]
308 Ok(Self::from_tagless(TaglessMemberId::deserialize(
309 deserializer,
310 )?))
311 }
312}
313
314impl<Tag> PartialOrd for MemberId<Tag> {
315 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
316 Some(self.cmp(other))
317 }
318}
319
320impl<Tag> Ord for MemberId<Tag> {
321 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
322 self.inner.cmp(&other.inner)
323 }
324}
325
326impl<Tag> PartialEq for MemberId<Tag> {
327 fn eq(&self, other: &Self) -> bool {
328 self.inner == other.inner
329 }
330}
331
332impl<Tag> Eq for MemberId<Tag> {}
333
334impl<Tag> Hash for MemberId<Tag> {
335 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
336 self.inner.hash(state);
337 std::any::type_name::<Tag>().hash(state);
340 }
341}