hydro_lang/location/
member_id.rs1use std::fmt::{Debug, Display};
2use std::hash::Hash;
3use std::marker::PhantomData;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Clone, Deserialize, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
8#[non_exhaustive] pub enum TaglessMemberId {
10 #[cfg(feature = "deploy_integration")]
11 #[cfg_attr(docsrs, doc(cfg(feature = "deploy_integration")))]
12 Legacy { raw_id: u32 },
13 #[cfg(feature = "docker_runtime")]
14 #[cfg_attr(docsrs, doc(cfg(feature = "docker_runtime")))]
15 Docker { container_name: String },
16 #[cfg(feature = "maelstrom_runtime")]
17 #[cfg_attr(docsrs, doc(cfg(feature = "maelstrom_runtime")))]
18 Maelstrom { node_id: String },
19}
20
21macro_rules! assert_feature {
22 (#[cfg(feature = $feat:expr)] $( $code:stmt )+) => {
23 #[cfg(not(feature = $feat))]
24 panic!("Feature {:?} is not enabled.", $feat);
25
26 #[cfg(feature = $feat)]
27 {
28 $( $code )+
29 }
30 };
31}
32
33impl TaglessMemberId {
34 pub fn from_raw_id(_raw_id: u32) -> Self {
35 assert_feature! {
36 #[cfg(feature = "deploy_integration")]
37 Self::Legacy { raw_id: _raw_id }
38 }
39 }
40
41 pub fn get_raw_id(&self) -> u32 {
42 assert_feature! {
43 #[cfg(feature = "deploy_integration")]
44 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
45 #[allow(
46 irrefutable_let_patterns,
47 reason = "Depends on features."
48 )]
49 let TaglessMemberId::Legacy { raw_id } = self else {
50 panic!("Not `Legacy` variant.");
51 }
52 *raw_id
53 }
54 }
55
56 pub fn from_container_name(_container_name: impl Into<String>) -> Self {
57 assert_feature! {
58 #[cfg(feature = "docker_runtime")]
59 Self::Docker {
60 container_name: _container_name.into(),
61 }
62 }
63 }
64
65 pub fn get_container_name(&self) -> &str {
66 assert_feature! {
67 #[cfg(feature = "docker_runtime")]
68 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
69 #[allow(
70 irrefutable_let_patterns,
71 reason = "Depends on features."
72 )]
73 let TaglessMemberId::Docker { container_name } = self else {
74 panic!("Not `Docker` variant.");
75 }
76 container_name
77 }
78 }
79
80 pub fn from_maelstrom_node_id(_node_id: impl Into<String>) -> Self {
81 assert_feature! {
82 #[cfg(feature = "maelstrom_runtime")]
83 Self::Maelstrom {
84 node_id: _node_id.into(),
85 }
86 }
87 }
88
89 pub fn get_maelstrom_node_id(&self) -> &str {
90 assert_feature! {
91 #[cfg(feature = "maelstrom_runtime")]
92 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
93 #[allow(
94 irrefutable_let_patterns,
95 reason = "Depends on features."
96 )]
97 let TaglessMemberId::Maelstrom { node_id } = self else {
98 panic!("Not `Maelstrom` variant.");
99 }
100 node_id
101 }
102 }
103}
104
105impl Display for TaglessMemberId {
106 fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 match self {
108 #[cfg(feature = "deploy_integration")]
109 TaglessMemberId::Legacy { raw_id } => write!(_f, "{:?}", raw_id),
110 #[cfg(feature = "docker_runtime")]
111 TaglessMemberId::Docker { container_name } => write!(_f, "{:?}", container_name),
112 #[cfg(feature = "maelstrom_runtime")]
113 TaglessMemberId::Maelstrom { node_id } => write!(_f, "{:?}", node_id),
114 #[expect(
115 clippy::allow_attributes,
116 reason = "Only triggers when `TaglessMemberId` is empty."
117 )]
118 #[allow(
119 unreachable_patterns,
120 reason = "Needed when `TaglessMemberId` is empty."
121 )]
122 _ => panic!(),
123 }
124 }
125}
126
127#[repr(transparent)]
128pub struct MemberId<Tag> {
129 inner: TaglessMemberId,
130 _phantom: PhantomData<Tag>,
131}
132
133impl<Tag> MemberId<Tag> {
134 pub fn into_tagless(self) -> TaglessMemberId {
135 self.inner
136 }
137
138 pub fn from_tagless(inner: TaglessMemberId) -> Self {
139 Self {
140 inner,
141 _phantom: Default::default(),
142 }
143 }
144
145 pub fn from_raw_id(raw_id: u32) -> Self {
146 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
147 #[allow(
148 unreachable_code,
149 reason = "`inner` may be uninhabited depending on features."
150 )]
151 Self {
152 inner: TaglessMemberId::from_raw_id(raw_id),
153 _phantom: Default::default(),
154 }
155 }
156
157 pub fn get_raw_id(&self) -> u32 {
158 self.inner.get_raw_id()
159 }
160}
161
162impl<Tag> Debug for MemberId<Tag> {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 Display::fmt(self, f)
165 }
166}
167
168impl<Tag> Display for MemberId<Tag> {
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170 write!(
171 f,
172 "MemberId::<{}>({})",
173 std::any::type_name::<Tag>(),
174 self.inner
175 )
176 }
177}
178
179impl<Tag> Clone for MemberId<Tag> {
180 fn clone(&self) -> Self {
181 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
182 #[allow(
183 unreachable_code,
184 reason = "`inner` may be uninhabited depending on features."
185 )]
186 Self {
187 inner: self.inner.clone(),
188 _phantom: Default::default(),
189 }
190 }
191}
192
193impl<Tag> Serialize for MemberId<Tag> {
194 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
195 where
196 S: serde::Serializer,
197 {
198 self.inner.serialize(serializer)
199 }
200}
201
202impl<'a, Tag> Deserialize<'a> for MemberId<Tag> {
203 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
204 where
205 D: serde::Deserializer<'a>,
206 {
207 #[expect(clippy::allow_attributes, reason = "Depends on features.")]
208 #[allow(
209 unreachable_code,
210 reason = "`inner` may be uninhabited depending on features."
211 )]
212 Ok(Self::from_tagless(TaglessMemberId::deserialize(
213 deserializer,
214 )?))
215 }
216}
217
218impl<Tag> PartialOrd for MemberId<Tag> {
219 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
220 Some(self.cmp(other))
221 }
222}
223
224impl<Tag> Ord for MemberId<Tag> {
225 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
226 self.inner.cmp(&other.inner)
227 }
228}
229
230impl<Tag> PartialEq for MemberId<Tag> {
231 fn eq(&self, other: &Self) -> bool {
232 self.inner == other.inner
233 }
234}
235
236impl<Tag> Eq for MemberId<Tag> {}
237
238impl<Tag> Hash for MemberId<Tag> {
239 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
240 self.inner.hash(state);
241 std::any::type_name::<Tag>().hash(state);
244 }
245}