1use std::fmt;
8
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10
11use crate::id::{id_to_hex, make_id};
12
13pub trait EntityId:
18 Copy + Eq + Ord + std::hash::Hash + std::fmt::Display + std::fmt::Debug
19{
20 fn raw(self) -> u128;
22
23 fn to_hex(self) -> String {
25 crate::id::id_to_hex(self.raw())
26 }
27}
28
29macro_rules! define_id_type {
31 ($(#[$meta:meta])* $name:ident, $domain:literal) => {
32 $(#[$meta])*
33 #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
34 pub struct $name(pub u128);
35
36 impl $name {
37 #[must_use]
39 pub const fn new(id: u128) -> Self {
40 Self(id)
41 }
42
43 #[must_use]
45 pub fn derive(data: &[u8]) -> Self {
46 Self(make_id($domain, data))
47 }
48
49 #[must_use]
51 pub const fn raw(self) -> u128 {
52 self.0
53 }
54
55 #[must_use]
57 pub fn to_hex(self) -> String {
58 id_to_hex(self.0)
59 }
60 }
61
62 impl fmt::Debug for $name {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 write!(f, "{}({})", stringify!($name), id_to_hex(self.0))
65 }
66 }
67
68 impl fmt::Display for $name {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 write!(f, "{}", id_to_hex(self.0))
71 }
72 }
73
74 impl From<u128> for $name {
75 fn from(id: u128) -> Self {
76 Self(id)
77 }
78 }
79
80 impl From<$name> for u128 {
81 fn from(id: $name) -> Self {
82 id.0
83 }
84 }
85
86 impl Serialize for $name {
87 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
88 serializer.serialize_str(&id_to_hex(self.0))
89 }
90 }
91
92 impl<'de> Deserialize<'de> for $name {
93 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
94 let s = String::deserialize(deserializer)?;
95 parse_hex_id(&s)
96 .map(Self)
97 .map_err(serde::de::Error::custom)
98 }
99 }
100
101 impl $crate::ids::EntityId for $name {
102 fn raw(self) -> u128 {
103 self.0
104 }
105 }
106 };
107}
108
109fn parse_hex_id(s: &str) -> Result<u128, String> {
111 let hex_str = s.strip_prefix("0x").unwrap_or(s);
112 u128::from_str_radix(hex_str, 16).map_err(|e| format!("invalid hex ID '{s}': {e}"))
113}
114
115define_id_type!(
116 ModuleId,
118 "module"
119);
120
121define_id_type!(
122 FunctionId,
124 "function"
125);
126
127define_id_type!(
128 BlockId,
130 "block"
131);
132
133define_id_type!(
134 InstId,
136 "inst"
137);
138
139define_id_type!(
140 ValueId,
142 "value"
143);
144
145define_id_type!(
146 ObjId,
148 "obj"
149);
150
151define_id_type!(
152 LocId,
154 "loc"
155);
156
157define_id_type!(
158 TypeId,
160 "type"
161);
162
163define_id_type!(
164 FileId,
166 "file"
167);
168
169define_id_type!(
170 ProgramId,
172 "program"
173);
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[test]
180 fn id_derive_is_deterministic() {
181 let a = FunctionId::derive(b"main");
182 let b = FunctionId::derive(b"main");
183 assert_eq!(a, b);
184 }
185
186 #[test]
187 fn different_domains_produce_different_ids() {
188 let fn_id = FunctionId::derive(b"main");
190 let block_id = BlockId::derive(b"main");
191 assert_ne!(fn_id.raw(), block_id.raw());
192 }
193
194 #[test]
195 fn id_display_format() {
196 let id = FunctionId::new(0x1234_5678_9abc_def0_1234_5678_9abc_def0);
197 let hex = id.to_string();
198 assert!(hex.starts_with("0x"));
199 assert_eq!(hex.len(), 34); }
201
202 #[test]
203 fn id_serialization_roundtrip() {
204 let original = FunctionId::derive(b"test_function");
205 let json = serde_json::to_string(&original).expect("serialize");
206 let parsed: FunctionId = serde_json::from_str(&json).expect("deserialize");
207 assert_eq!(original, parsed);
208 }
209
210 #[test]
211 fn id_parse_with_and_without_prefix() {
212 let id = FunctionId::new(0x123);
213
214 let json_with_prefix = "\"0x00000000000000000000000000000123\"";
216 let parsed: FunctionId = serde_json::from_str(json_with_prefix).expect("parse with prefix");
217 assert_eq!(parsed, id);
218
219 let json_without_prefix = "\"00000000000000000000000000000123\"";
221 let parsed: FunctionId =
222 serde_json::from_str(json_without_prefix).expect("parse without prefix");
223 assert_eq!(parsed, id);
224 }
225
226 #[test]
227 fn id_ordering() {
228 let a = FunctionId::new(1);
229 let b = FunctionId::new(2);
230 let c = FunctionId::new(1);
231 assert!(a < b);
232 assert_eq!(a, c);
233 }
234
235 #[test]
236 fn type_id_derive_is_deterministic() {
237 let a = TypeId::derive(b"integer:32");
238 let b = TypeId::derive(b"integer:32");
239 assert_eq!(a, b);
240 }
241
242 #[test]
243 fn type_id_different_from_other_domains() {
244 let type_id = TypeId::derive(b"test");
245 let value_id = ValueId::derive(b"test");
246 assert_ne!(type_id.raw(), value_id.raw());
247 }
248
249 #[test]
250 fn entity_id_trait_works() {
251 let func_id = FunctionId::derive(b"test");
252 let raw: u128 = EntityId::raw(func_id);
254 assert_eq!(raw, func_id.0);
255 let hex: String = EntityId::to_hex(func_id);
256 assert!(hex.starts_with("0x"));
257 assert_eq!(hex.len(), 34);
258 }
259
260 #[test]
261 fn entity_id_trait_is_generic() {
262 fn check_id<T: EntityId>(id: T) -> u128 {
264 id.raw()
265 }
266 let fid = FunctionId::new(42);
267 let bid = BlockId::new(42);
268 assert_eq!(check_id(fid), check_id(bid));
270 }
271}