ocaml_interop/conv/
from_ocaml.rs

1// Copyright (c) Viable Systems and TezEdge Contributors
2// SPDX-License-Identifier: MIT
3
4use crate::{
5    mlvalues::{
6        field_val, tag, OCamlBytes, OCamlFloat, OCamlFloatArray, OCamlInt, OCamlInt32, OCamlInt64,
7        OCamlList, OCamlUniformArray,
8    },
9    value::OCaml,
10};
11use ocaml_sys::caml_sys_double_field;
12
13/// Implements conversion from OCaml values into Rust values.
14///
15/// # Safety
16///
17/// Implementing this trait involves unsafe code that interacts with the OCaml runtime.
18/// Implementors must adhere to the following safety guidelines:
19///
20/// - **Valid OCaml Values**: The OCaml value passed to the `from_ocaml` function must be valid.
21///   The implementor is responsible for ensuring that the value is a correct and valid representation
22///   of the type `T` in OCaml. Passing an invalid or unrelated value may lead to undefined behavior.
23///
24/// - **Handling of OCaml Exceptions**: If the OCaml code can raise exceptions, the implementor
25///   must ensure these are appropriately handled. Uncaught OCaml exceptions should not be allowed
26///   to propagate into the Rust code, as they are not compatible with Rust's error handling mechanisms.
27///
28/// Implementors of this trait need to have a thorough understanding of the OCaml runtime, especially
29/// regarding value representation and memory management, to ensure safe and correct conversions
30/// from OCaml to Rust.
31pub unsafe trait FromOCaml<T> {
32    /// Convert from OCaml value.
33    fn from_ocaml(v: OCaml<T>) -> Self;
34}
35
36unsafe impl FromOCaml<()> for () {
37    fn from_ocaml(_v: OCaml<()>) -> Self {
38        // Nothing, just unit
39    }
40}
41
42unsafe impl FromOCaml<OCamlInt> for i64 {
43    fn from_ocaml(v: OCaml<OCamlInt>) -> Self {
44        v.to_i64()
45    }
46}
47
48unsafe impl FromOCaml<OCamlInt> for i32 {
49    fn from_ocaml(v: OCaml<OCamlInt>) -> Self {
50        v.to_i64() as i32
51    }
52}
53
54unsafe impl FromOCaml<OCamlInt32> for i32 {
55    fn from_ocaml(v: OCaml<OCamlInt32>) -> Self {
56        let val = unsafe { field_val(v.raw(), 1) };
57        unsafe { *(val as *const i32) }
58    }
59}
60
61unsafe impl FromOCaml<OCamlInt64> for i64 {
62    fn from_ocaml(v: OCaml<OCamlInt64>) -> Self {
63        let val = unsafe { field_val(v.raw(), 1) };
64        unsafe { *(val as *const i64) }
65    }
66}
67
68unsafe impl FromOCaml<bool> for bool {
69    fn from_ocaml(v: OCaml<bool>) -> Self {
70        v.to_bool()
71    }
72}
73
74unsafe impl FromOCaml<OCamlFloat> for f64 {
75    fn from_ocaml(v: OCaml<OCamlFloat>) -> Self {
76        unsafe { *(v.raw() as *const f64) }
77    }
78}
79
80unsafe impl FromOCaml<String> for Vec<u8> {
81    fn from_ocaml(v: OCaml<String>) -> Self {
82        let raw_bytes = v.as_bytes();
83        let mut vec: Vec<u8> = Vec::with_capacity(raw_bytes.len());
84        vec.extend_from_slice(raw_bytes);
85        vec
86    }
87}
88
89unsafe impl FromOCaml<String> for String {
90    fn from_ocaml(v: OCaml<String>) -> Self {
91        String::from_utf8_lossy(v.as_bytes()).into_owned()
92    }
93}
94
95unsafe impl FromOCaml<OCamlBytes> for Vec<u8> {
96    fn from_ocaml(v: OCaml<OCamlBytes>) -> Self {
97        let raw_bytes = v.as_bytes();
98        let mut vec: Vec<u8> = Vec::with_capacity(raw_bytes.len());
99        vec.extend_from_slice(raw_bytes);
100        vec
101    }
102}
103
104unsafe impl FromOCaml<OCamlBytes> for Box<[u8]> {
105    fn from_ocaml(v: OCaml<OCamlBytes>) -> Self {
106        let raw_bytes = v.as_bytes();
107        Box::from(raw_bytes)
108    }
109}
110
111unsafe impl FromOCaml<OCamlBytes> for String {
112    fn from_ocaml(v: OCaml<OCamlBytes>) -> Self {
113        unsafe { v.as_str_unchecked() }.to_owned()
114    }
115}
116
117unsafe impl<OCamlT, T: FromOCaml<OCamlT>> FromOCaml<OCamlT> for Box<T> {
118    fn from_ocaml(v: OCaml<OCamlT>) -> Self {
119        Box::new(T::from_ocaml(v))
120    }
121}
122
123unsafe impl<A, OCamlA, Err, OCamlErr> FromOCaml<Result<OCamlA, OCamlErr>> for Result<A, Err>
124where
125    A: FromOCaml<OCamlA>,
126    Err: FromOCaml<OCamlErr>,
127{
128    fn from_ocaml(v: OCaml<Result<OCamlA, OCamlErr>>) -> Self {
129        match v.to_result() {
130            Ok(ocaml_ok) => Ok(A::from_ocaml(ocaml_ok)),
131            Err(ocaml_err) => Err(Err::from_ocaml(ocaml_err)),
132        }
133    }
134}
135
136unsafe impl<A, OCamlA> FromOCaml<Option<OCamlA>> for Option<A>
137where
138    A: FromOCaml<OCamlA>,
139{
140    fn from_ocaml(v: OCaml<Option<OCamlA>>) -> Self {
141        v.to_option().map(A::from_ocaml)
142    }
143}
144
145unsafe impl<A, OCamlA> FromOCaml<OCamlList<OCamlA>> for Vec<A>
146where
147    A: FromOCaml<OCamlA>,
148{
149    fn from_ocaml(v: OCaml<OCamlList<OCamlA>>) -> Self {
150        // TODO: pre-calculate actual required capacity?
151        let mut vec = Vec::new();
152        let mut current = v;
153        while let Some((hd, tl)) = current.uncons() {
154            current = tl;
155            vec.push(A::from_ocaml(hd));
156        }
157        vec
158    }
159}
160
161unsafe impl<A, OCamlA> FromOCaml<OCamlUniformArray<OCamlA>> for Vec<A>
162where
163    A: FromOCaml<OCamlA>,
164{
165    fn from_ocaml(v: OCaml<OCamlUniformArray<OCamlA>>) -> Self {
166        assert!(
167            v.tag_value() != tag::DOUBLE_ARRAY,
168            "unboxed float arrays are not supported"
169        );
170
171        let size = unsafe { v.size() };
172        let mut vec = Vec::with_capacity(size);
173        for i in 0..size {
174            vec.push(A::from_ocaml(unsafe { v.field(i) }));
175        }
176        vec
177    }
178}
179
180unsafe impl FromOCaml<OCamlFloatArray> for Vec<f64> {
181    fn from_ocaml(v: OCaml<OCamlFloatArray>) -> Self {
182        let size = unsafe { v.size() };
183
184        // an empty floatarray doesn't have the double array tag, but otherwise
185        // we always expect an unboxed float array.
186        if size > 0 {
187            assert_eq!(v.tag_value(), tag::DOUBLE_ARRAY)
188        };
189
190        let mut vec = Vec::with_capacity(size);
191        for i in 0..size {
192            vec.push(unsafe { caml_sys_double_field(v.raw(), i) });
193        }
194        vec
195    }
196}
197
198// Tuples
199
200macro_rules! tuple_from_ocaml {
201    ($($accessor:ident: $t:ident => $ot:ident),+) => {
202        unsafe impl<$($t),+, $($ot: 'static),+> FromOCaml<($($ot),+)> for ($($t),+)
203        where
204            $($t: FromOCaml<$ot>),+
205        {
206            fn from_ocaml(v: OCaml<($($ot),+)>) -> Self {
207                ($($t::from_ocaml(v.$accessor())),+)
208
209            }
210        }
211    };
212}
213
214tuple_from_ocaml!(
215    fst: OCamlA => A,
216    snd: OCamlB => B);
217tuple_from_ocaml!(
218    fst: OCamlA => A,
219    snd: OCamlB => B,
220    tuple_3: OCamlC => C);
221tuple_from_ocaml!(
222    fst: OCamlA => A,
223    snd: OCamlB => B,
224    tuple_3: OCamlC => C,
225    tuple_4: OCamlD => D);
226tuple_from_ocaml!(
227    fst: OCamlA => A,
228    snd: OCamlB => B,
229    tuple_3: OCamlC => C,
230    tuple_4: OCamlD => D,
231    tuple_5: OCamlE => E);
232tuple_from_ocaml!(
233    fst: OCamlA => A,
234    snd: OCamlB => B,
235    tuple_3: OCamlC => C,
236    tuple_4: OCamlD => D,
237    tuple_5: OCamlE => E,
238    tuple_6: OCamlF => F);
239tuple_from_ocaml!(
240    fst: OCamlA => A,
241    snd: OCamlB => B,
242    tuple_3: OCamlC => C,
243    tuple_4: OCamlD => D,
244    tuple_5: OCamlE => E,
245    tuple_6: OCamlF => F,
246    tuple_7: OCamlG => G);
247tuple_from_ocaml!(
248    fst: OCamlA => A,
249    snd: OCamlB => B,
250    tuple_3: OCamlC => C,
251    tuple_4: OCamlD => D,
252    tuple_5: OCamlE => E,
253    tuple_6: OCamlF => F,
254    tuple_7: OCamlG => G,
255    tuple_8: OCamlH => H);
256tuple_from_ocaml!(
257    fst: OCamlA => A,
258    snd: OCamlB => B,
259    tuple_3: OCamlC => C,
260    tuple_4: OCamlD => D,
261    tuple_5: OCamlE => E,
262    tuple_6: OCamlF => F,
263    tuple_7: OCamlG => G,
264    tuple_8: OCamlH => H,
265    tuple_9: OCamlI => I);