Skip to main content

shape_value/v2/
typed_result.rs

1//! Typed Result representations for v2 runtime.
2//!
3//! Result<T, E> is a tagged union: tag (0=Ok, 1=Err) + payload.
4//! Size = 8 + max(sizeof(T), sizeof(E)). Monomorphized per instantiation.
5//!
6//! Since Rust `#[repr(C)]` unions with generics are awkward, we provide
7//! concrete instantiations for common Shape result types.
8
9use super::string_obj::StringObj;
10
11/// Tag value for Ok variant.
12pub const RESULT_TAG_OK: u8 = 0;
13/// Tag value for Err variant.
14pub const RESULT_TAG_ERR: u8 = 1;
15
16/// Result<f64, *const StringObj> — Ok is f64, Err is string pointer.
17///
18/// ## Memory layout (16 bytes)
19/// ```text
20/// Offset  Size  Field
21/// ------  ----  -----
22///   0       1   tag (0=Ok, 1=Err)
23///   1       7   _pad
24///   8       8   payload (f64 or *const StringObj)
25/// ```
26#[repr(C)]
27pub struct ResultF64Str {
28    pub tag: u8,
29    pub _pad: [u8; 7],
30    payload: u64, // union: f64 bits or pointer
31}
32
33impl ResultF64Str {
34    /// Create an Ok(f64) result.
35    #[inline]
36    pub fn ok(value: f64) -> Self {
37        Self {
38            tag: RESULT_TAG_OK,
39            _pad: [0; 7],
40            payload: value.to_bits(),
41        }
42    }
43
44    /// Create an Err(*const StringObj) result.
45    #[inline]
46    pub fn err(err_ptr: *const StringObj) -> Self {
47        Self {
48            tag: RESULT_TAG_ERR,
49            _pad: [0; 7],
50            payload: err_ptr as u64,
51        }
52    }
53
54    #[inline]
55    pub fn is_ok(&self) -> bool {
56        self.tag == RESULT_TAG_OK
57    }
58
59    #[inline]
60    pub fn is_err(&self) -> bool {
61        self.tag == RESULT_TAG_ERR
62    }
63
64    /// Get the Ok value. Caller must check `is_ok()` first.
65    #[inline]
66    pub fn unwrap_ok(&self) -> f64 {
67        debug_assert!(self.is_ok());
68        f64::from_bits(self.payload)
69    }
70
71    /// Get the Err pointer. Caller must check `is_err()` first.
72    #[inline]
73    pub fn unwrap_err(&self) -> *const StringObj {
74        debug_assert!(self.is_err());
75        self.payload as *const StringObj
76    }
77}
78
79/// Result<i64, *const StringObj> — Ok is i64, Err is string pointer.
80///
81/// ## Memory layout (16 bytes)
82/// ```text
83/// Offset  Size  Field
84/// ------  ----  -----
85///   0       1   tag (0=Ok, 1=Err)
86///   1       7   _pad
87///   8       8   payload (i64 or *const StringObj)
88/// ```
89#[repr(C)]
90pub struct ResultI64Str {
91    pub tag: u8,
92    pub _pad: [u8; 7],
93    payload: u64, // union: i64 bits or pointer
94}
95
96impl ResultI64Str {
97    /// Create an Ok(i64) result.
98    #[inline]
99    pub fn ok(value: i64) -> Self {
100        Self {
101            tag: RESULT_TAG_OK,
102            _pad: [0; 7],
103            payload: value as u64,
104        }
105    }
106
107    /// Create an Err(*const StringObj) result.
108    #[inline]
109    pub fn err(err_ptr: *const StringObj) -> Self {
110        Self {
111            tag: RESULT_TAG_ERR,
112            _pad: [0; 7],
113            payload: err_ptr as u64,
114        }
115    }
116
117    #[inline]
118    pub fn is_ok(&self) -> bool {
119        self.tag == RESULT_TAG_OK
120    }
121
122    #[inline]
123    pub fn is_err(&self) -> bool {
124        self.tag == RESULT_TAG_ERR
125    }
126
127    /// Get the Ok value. Caller must check `is_ok()` first.
128    #[inline]
129    pub fn unwrap_ok(&self) -> i64 {
130        debug_assert!(self.is_ok());
131        self.payload as i64
132    }
133
134    /// Get the Err pointer. Caller must check `is_err()` first.
135    #[inline]
136    pub fn unwrap_err(&self) -> *const StringObj {
137        debug_assert!(self.is_err());
138        self.payload as *const StringObj
139    }
140}
141
142/// Result<*const T, *const StringObj> — Ok is a heap pointer, Err is string pointer.
143///
144/// ## Memory layout (16 bytes)
145/// ```text
146/// Offset  Size  Field
147/// ------  ----  -----
148///   0       1   tag (0=Ok, 1=Err)
149///   1       7   _pad
150///   8       8   payload (*const T or *const StringObj)
151/// ```
152#[repr(C)]
153pub struct ResultPtrStr {
154    pub tag: u8,
155    pub _pad: [u8; 7],
156    payload: u64, // union: ok pointer or err pointer
157}
158
159impl ResultPtrStr {
160    /// Create an Ok(*const u8) result (generic heap pointer).
161    #[inline]
162    pub fn ok(ptr: *const u8) -> Self {
163        Self {
164            tag: RESULT_TAG_OK,
165            _pad: [0; 7],
166            payload: ptr as u64,
167        }
168    }
169
170    /// Create an Err(*const StringObj) result.
171    #[inline]
172    pub fn err(err_ptr: *const StringObj) -> Self {
173        Self {
174            tag: RESULT_TAG_ERR,
175            _pad: [0; 7],
176            payload: err_ptr as u64,
177        }
178    }
179
180    #[inline]
181    pub fn is_ok(&self) -> bool {
182        self.tag == RESULT_TAG_OK
183    }
184
185    #[inline]
186    pub fn is_err(&self) -> bool {
187        self.tag == RESULT_TAG_ERR
188    }
189
190    /// Get the Ok pointer. Caller must check `is_ok()` first.
191    #[inline]
192    pub fn unwrap_ok(&self) -> *const u8 {
193        debug_assert!(self.is_ok());
194        self.payload as *const u8
195    }
196
197    /// Get the Err pointer. Caller must check `is_err()` first.
198    #[inline]
199    pub fn unwrap_err(&self) -> *const StringObj {
200        debug_assert!(self.is_err());
201        self.payload as *const StringObj
202    }
203}
204
205/// Byte offset constants for JIT codegen.
206pub const RESULT_OFFSET_TAG: usize = 0;
207pub const RESULT_OFFSET_PAYLOAD: usize = 8;
208
209#[cfg(test)]
210mod tests {
211    use super::*;
212
213    // --- ResultF64Str ---
214
215    #[test]
216    fn test_result_f64_ok() {
217        let r = ResultF64Str::ok(3.14);
218        assert!(r.is_ok());
219        assert!(!r.is_err());
220        assert!((r.unwrap_ok() - 3.14).abs() < f64::EPSILON);
221    }
222
223    #[test]
224    fn test_result_f64_err() {
225        let err = StringObj::new("something went wrong");
226        let r = ResultF64Str::err(err);
227        assert!(r.is_err());
228        assert!(!r.is_ok());
229        assert_eq!(r.unwrap_err(), err as *const StringObj);
230        unsafe { StringObj::drop(err) };
231    }
232
233    #[test]
234    fn test_size_of_result_f64_str() {
235        assert_eq!(std::mem::size_of::<ResultF64Str>(), 16);
236    }
237
238    // --- ResultI64Str ---
239
240    #[test]
241    fn test_result_i64_ok() {
242        let r = ResultI64Str::ok(42);
243        assert!(r.is_ok());
244        assert_eq!(r.unwrap_ok(), 42);
245    }
246
247    #[test]
248    fn test_result_i64_ok_negative() {
249        let r = ResultI64Str::ok(-100);
250        assert!(r.is_ok());
251        assert_eq!(r.unwrap_ok(), -100);
252    }
253
254    #[test]
255    fn test_result_i64_err() {
256        let err = StringObj::new("i64 error");
257        let r = ResultI64Str::err(err);
258        assert!(r.is_err());
259        assert_eq!(r.unwrap_err(), err as *const StringObj);
260        unsafe { StringObj::drop(err) };
261    }
262
263    #[test]
264    fn test_size_of_result_i64_str() {
265        assert_eq!(std::mem::size_of::<ResultI64Str>(), 16);
266    }
267
268    // --- ResultPtrStr ---
269
270    #[test]
271    fn test_result_ptr_ok() {
272        let val: u8 = 99;
273        let r = ResultPtrStr::ok(&val as *const u8);
274        assert!(r.is_ok());
275        assert_eq!(r.unwrap_ok(), &val as *const u8);
276    }
277
278    #[test]
279    fn test_result_ptr_err() {
280        let err = StringObj::new("ptr error");
281        let r = ResultPtrStr::err(err);
282        assert!(r.is_err());
283        assert_eq!(r.unwrap_err(), err as *const StringObj);
284        unsafe { StringObj::drop(err) };
285    }
286
287    #[test]
288    fn test_size_of_result_ptr_str() {
289        assert_eq!(std::mem::size_of::<ResultPtrStr>(), 16);
290    }
291
292    // --- Offset constants ---
293
294    #[test]
295    fn test_result_field_offsets() {
296        let r = ResultF64Str::ok(1.0);
297        let base = &r as *const _ as usize;
298        let tag_offset = &r.tag as *const _ as usize - base;
299        let payload_offset = &r.payload as *const _ as usize - base;
300
301        assert_eq!(tag_offset, RESULT_OFFSET_TAG);
302        assert_eq!(payload_offset, RESULT_OFFSET_PAYLOAD);
303    }
304
305    // --- Tag constants ---
306
307    #[test]
308    fn test_tag_constants() {
309        assert_eq!(RESULT_TAG_OK, 0);
310        assert_eq!(RESULT_TAG_ERR, 1);
311        assert_ne!(RESULT_TAG_OK, RESULT_TAG_ERR);
312    }
313}