Skip to main content

shape_value/
ids.rs

1//! Strongly-typed newtype wrappers for numeric identifiers.
2//!
3//! These newtypes prevent accidental misuse of raw `u16`/`u32`/`u64` values
4//! in different identifier domains (function IDs, string pool indices, schema IDs, etc.).
5
6use serde::{Deserialize, Serialize};
7
8/// A function identifier. Indexes into `BytecodeProgram::functions`.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
10#[repr(transparent)]
11pub struct FunctionId(pub u16);
12
13impl FunctionId {
14    /// Create a new FunctionId from a raw u16.
15    #[inline]
16    pub const fn new(id: u16) -> Self {
17        Self(id)
18    }
19
20    /// Get the raw u16 value.
21    #[inline]
22    pub const fn raw(self) -> u16 {
23        self.0
24    }
25
26    /// Convert to usize for indexing.
27    #[inline]
28    pub const fn index(self) -> usize {
29        self.0 as usize
30    }
31}
32
33impl From<u16> for FunctionId {
34    #[inline]
35    fn from(id: u16) -> Self {
36        Self(id)
37    }
38}
39
40impl From<FunctionId> for u16 {
41    #[inline]
42    fn from(id: FunctionId) -> u16 {
43        id.0
44    }
45}
46
47impl From<FunctionId> for usize {
48    #[inline]
49    fn from(id: FunctionId) -> usize {
50        id.0 as usize
51    }
52}
53
54impl std::fmt::Display for FunctionId {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        write!(f, "fn#{}", self.0)
57    }
58}
59
60/// A string pool identifier. Indexes into `BytecodeProgram::strings`.
61///
62/// Using `StringId` instead of a heap-allocated `String` makes
63/// `Operand` (and therefore `Instruction`) `Copy`.
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
65#[repr(transparent)]
66pub struct StringId(pub u32);
67
68impl StringId {
69    /// Create a new StringId from a raw u32.
70    #[inline]
71    pub const fn new(id: u32) -> Self {
72        Self(id)
73    }
74
75    /// Get the raw u32 value.
76    #[inline]
77    pub const fn raw(self) -> u32 {
78        self.0
79    }
80
81    /// Convert to usize for indexing.
82    #[inline]
83    pub const fn index(self) -> usize {
84        self.0 as usize
85    }
86}
87
88impl From<u32> for StringId {
89    #[inline]
90    fn from(id: u32) -> Self {
91        Self(id)
92    }
93}
94
95impl From<StringId> for u32 {
96    #[inline]
97    fn from(id: StringId) -> u32 {
98        id.0
99    }
100}
101
102impl From<StringId> for usize {
103    #[inline]
104    fn from(id: StringId) -> usize {
105        id.0 as usize
106    }
107}
108
109impl std::fmt::Display for StringId {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        write!(f, "str#{}", self.0)
112    }
113}
114
115/// A type schema identifier. Indexes into the type schema registry.
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
117#[repr(transparent)]
118pub struct SchemaId(pub u32);
119
120impl SchemaId {
121    /// Create a new SchemaId from a raw u32.
122    #[inline]
123    pub const fn new(id: u32) -> Self {
124        Self(id)
125    }
126
127    /// Get the raw u32 value.
128    #[inline]
129    pub const fn raw(self) -> u32 {
130        self.0
131    }
132
133    /// Convert to usize for indexing.
134    #[inline]
135    pub const fn index(self) -> usize {
136        self.0 as usize
137    }
138}
139
140impl From<u32> for SchemaId {
141    #[inline]
142    fn from(id: u32) -> Self {
143        Self(id)
144    }
145}
146
147impl From<SchemaId> for u32 {
148    #[inline]
149    fn from(id: SchemaId) -> u32 {
150        id.0
151    }
152}
153
154impl From<SchemaId> for usize {
155    #[inline]
156    fn from(id: SchemaId) -> usize {
157        id.0 as usize
158    }
159}
160
161impl std::fmt::Display for SchemaId {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        write!(f, "schema#{}", self.0)
164    }
165}
166
167/// A stack slot index. Used for local variables and temporary values.
168#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
169#[repr(transparent)]
170pub struct StackSlotIdx(pub usize);
171
172impl StackSlotIdx {
173    /// Create a new StackSlotIdx.
174    #[inline]
175    pub const fn new(idx: usize) -> Self {
176        Self(idx)
177    }
178
179    /// Get the raw usize value.
180    #[inline]
181    pub const fn raw(self) -> usize {
182        self.0
183    }
184}
185
186impl From<usize> for StackSlotIdx {
187    #[inline]
188    fn from(idx: usize) -> Self {
189        Self(idx)
190    }
191}
192
193impl From<StackSlotIdx> for usize {
194    #[inline]
195    fn from(idx: StackSlotIdx) -> usize {
196        idx.0
197    }
198}
199
200impl std::fmt::Display for StackSlotIdx {
201    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202        write!(f, "slot#{}", self.0)
203    }
204}
205
206#[cfg(test)]
207mod tests {
208    use super::*;
209
210    #[test]
211    fn test_function_id_conversions() {
212        let id = FunctionId::new(42);
213        assert_eq!(id.raw(), 42u16);
214        assert_eq!(id.index(), 42usize);
215        assert_eq!(u16::from(id), 42u16);
216        assert_eq!(usize::from(id), 42usize);
217        assert_eq!(FunctionId::from(42u16), id);
218    }
219
220    #[test]
221    fn test_string_id_conversions() {
222        let id = StringId::new(100);
223        assert_eq!(id.raw(), 100u32);
224        assert_eq!(id.index(), 100usize);
225        assert_eq!(u32::from(id), 100u32);
226        assert_eq!(usize::from(id), 100usize);
227        assert_eq!(StringId::from(100u32), id);
228    }
229
230    #[test]
231    fn test_schema_id_conversions() {
232        let id = SchemaId::new(7);
233        assert_eq!(id.raw(), 7u32);
234        assert_eq!(id.index(), 7usize);
235        assert_eq!(u32::from(id), 7u32);
236        assert_eq!(SchemaId::from(7u32), id);
237    }
238
239    #[test]
240    fn test_display() {
241        assert_eq!(format!("{}", FunctionId::new(5)), "fn#5");
242        assert_eq!(format!("{}", StringId::new(10)), "str#10");
243        assert_eq!(format!("{}", SchemaId::new(3)), "schema#3");
244        assert_eq!(format!("{}", StackSlotIdx::new(0)), "slot#0");
245    }
246
247    #[test]
248    fn test_different_types_not_comparable() {
249        // This is a compile-time check — these should NOT compile:
250        // let _: FunctionId = StringId::new(1); // error: mismatched types
251        // let _: StringId = FunctionId::new(1); // error: mismatched types
252        // Just verify they're different types
253        let fn_id = FunctionId::new(1);
254        let str_id = StringId::new(1);
255        assert_ne!(
256            std::any::TypeId::of::<FunctionId>(),
257            std::any::TypeId::of::<StringId>()
258        );
259        // Can't accidentally mix them, even though the raw values are the same
260        let _ = (fn_id, str_id);
261    }
262}