ocaml_interop/macros.rs
1// Copyright (c) Viable Systems and TezEdge Contributors
2// SPDX-License-Identifier: MIT
3
4#[cfg(doc)]
5use crate::*;
6
7/// Declares OCaml functions.
8///
9/// `ocaml! { pub fn registered_name(arg1: ArgT, ...) -> Ret_typ; ... }` declares a function that has been
10/// defined in OCaml code and registered with `Callback.register "registered_name" ocaml_function`.
11///
12/// Visibility and return value type can be omitted. The return type defaults to `()` when omitted.
13///
14/// When invoking one of these functions, the first argument must be a `&mut `[`OCamlRuntime`],
15/// and the remaining arguments [`OCamlRef`]`<ArgT>`.
16///
17/// The return value is a [`BoxRoot`]`<RetType>`.
18///
19/// Calls that raise an OCaml exception will `panic!`. Care must be taken on the OCaml side
20/// to avoid exceptions and return `('a, 'err) Result.t` values to signal errors, which
21/// can then be converted into Rust's `Result<A, Err>` and `Result<OCaml<A>, OCaml<Err>>`.
22///
23/// # Examples
24///
25/// ```
26/// # use ocaml_interop::*;
27/// # struct MyRecord {};
28/// ocaml! {
29/// // Declares `print_endline`, with a single `String` (`OCamlRef<String>` when invoked)
30/// // argument and `BoxRoot<()>` return type (default when omitted).
31/// pub fn print_endline(s: String);
32///
33/// // Declares `bytes_concat`, with two arguments, an OCaml `bytes` separator,
34/// // and an OCaml list of segments to concatenate. Return value is an OCaml `bytes`
35/// // value.
36/// fn bytes_concat(sep: OCamlBytes, segments: OCamlList<OCamlBytes>) -> OCamlBytes;
37/// }
38/// ```
39#[macro_export]
40macro_rules! ocaml {
41 () => ();
42
43 ($vis:vis fn $name:ident(
44 $arg:ident: $typ:ty $(,)?
45 ) $(-> $rtyp:ty)?; $($t:tt)*) => {
46 $vis fn $name<'a>(
47 cr: &'a mut $crate::OCamlRuntime,
48 $arg: $crate::OCamlRef<$typ>,
49 ) -> $crate::BoxRoot<$crate::default_to_unit!($($rtyp)?)> {
50 $crate::ocaml_closure_reference!(closure, $name);
51 $crate::BoxRoot::new(closure.call(cr, $arg))
52 }
53
54 $crate::ocaml!($($t)*);
55 };
56
57 ($vis:vis fn $name:ident(
58 $arg1:ident: $typ1:ty,
59 $arg2:ident: $typ2:ty $(,)?
60 ) $(-> $rtyp:ty)?; $($t:tt)*) => {
61 $vis fn $name<'a>(
62 cr: &'a mut $crate::OCamlRuntime,
63 $arg1: $crate::OCamlRef<$typ1>,
64 $arg2: $crate::OCamlRef<$typ2>,
65 ) -> $crate::BoxRoot<$crate::default_to_unit!($($rtyp)?)> {
66 $crate::ocaml_closure_reference!(closure, $name);
67 $crate::BoxRoot::new(closure.call2(cr, $arg1, $arg2))
68 }
69
70 $crate::ocaml!($($t)*);
71 };
72
73 ($vis:vis fn $name:ident(
74 $arg1:ident: $typ1:ty,
75 $arg2:ident: $typ2:ty,
76 $arg3:ident: $typ3:ty $(,)?
77 ) $(-> $rtyp:ty)?; $($t:tt)*) => {
78 $vis fn $name<'a>(
79 cr: &'a mut $crate::OCamlRuntime,
80 $arg1: $crate::OCamlRef<$typ1>,
81 $arg2: $crate::OCamlRef<$typ2>,
82 $arg3: $crate::OCamlRef<$typ3>,
83 ) -> $crate::BoxRoot<$crate::default_to_unit!($($rtyp)?)> {
84 $crate::ocaml_closure_reference!(closure, $name);
85 $crate::BoxRoot::new(closure.call3(cr, $arg1, $arg2, $arg3))
86 }
87
88 $crate::ocaml!($($t)*);
89 };
90
91 ($vis:vis fn $name:ident(
92 $($arg:ident: $typ:ty),+ $(,)?
93 ) $(-> $rtyp:ty)?; $($t:tt)*) => {
94 $vis fn $name<'a>(
95 cr: &'a mut $crate::OCamlRuntime,
96 $($arg: $crate::OCamlRef<$typ>),+
97 ) -> $crate::BoxRoot<$crate::default_to_unit!($($rtyp)?)> {
98 $crate::ocaml_closure_reference!(closure, $name);
99 $crate::BoxRoot::new(closure.call_n(cr, &mut [$(unsafe { $arg.get_raw() }),+]))
100 }
101
102 $crate::ocaml!($($t)*);
103 }
104}
105
106/// Implements conversion between a Rust struct and an OCaml record.
107///
108/// See the [`impl_to_ocaml_record!`] and [`impl_from_ocaml_record!`] macros
109/// for more details.
110#[macro_export]
111macro_rules! impl_conv_ocaml_record {
112 ($rust_typ:ident => $ocaml_typ:ident {
113 $($field:ident : $ocaml_field_typ:ty $(=> $conv_expr:expr)?),+ $(,)?
114 }) => {
115 $crate::impl_to_ocaml_record! {
116 $rust_typ => $ocaml_typ {
117 $($field : $ocaml_field_typ $(=> $conv_expr)?),+
118 }
119 }
120
121 $crate::impl_from_ocaml_record! {
122 $ocaml_typ => $rust_typ {
123 $($field : $ocaml_field_typ),+
124 }
125 }
126 };
127
128 ($both_typ:ident {
129 $($t:tt)*
130 }) => {
131 $crate::impl_conv_ocaml_record! {
132 $both_typ => $both_typ {
133 $($t)*
134 }
135 }
136 };
137}
138
139/// Implements conversion between a Rust enum and an OCaml variant.
140///
141/// See the [`impl_to_ocaml_variant!`] and [`impl_from_ocaml_variant!`] macros
142/// for more details.
143#[macro_export]
144macro_rules! impl_conv_ocaml_variant {
145 ($rust_typ:ty => $ocaml_typ:ty {
146 $($($tag:ident)::+ $(($($slot_name:ident: $slot_typ:ty),+ $(,)?))? $(=> $conv:expr)?),+ $(,)?
147 }) => {
148 $crate::impl_to_ocaml_variant! {
149 $rust_typ => $ocaml_typ {
150 $($($tag)::+ $(($($slot_name: $slot_typ),+))? $(=> $conv)?),+
151 }
152 }
153
154 $crate::impl_from_ocaml_variant! {
155 $ocaml_typ => $rust_typ {
156 $($($tag)::+ $(($($slot_name: $slot_typ),+))?),+
157 }
158 }
159 };
160
161 ($both_typ:ty {
162 $($t:tt)*
163 }) => {
164 $crate::impl_conv_ocaml_variant!{
165 $both_typ => $both_typ {
166 $($t)*
167 }
168 }
169 };
170}
171
172/// Unpacks an OCaml record into a Rust record.
173///
174/// This macro works on [`OCaml`]`<'gc, T>` values.
175///
176/// It is important that the order of the fields remains the same as in the OCaml type declaration.
177///
178/// # Examples
179///
180/// ```
181/// # use ocaml_interop::*;
182/// # ocaml! { fn make_mystruct(unit: ()) -> MyStruct; }
183/// struct MyStruct {
184/// int_field: i64,
185/// string_field: String,
186/// }
187///
188/// // Assuming an OCaml record declaration like:
189/// //
190/// // type my_struct = {
191/// // int_field: int;
192/// // string_field: string;
193/// // }
194/// //
195/// // NOTE: What is important is the order of the fields, not their names.
196///
197/// # fn unpack_record_example(cr: &mut OCamlRuntime) {
198/// let ocaml_struct_root = make_mystruct(cr, &OCaml::unit());
199/// let ocaml_struct = cr.get(&ocaml_struct_root);
200/// let my_struct = ocaml_unpack_record! {
201/// // value => RustConstructor { field: OCamlType, ... }
202/// ocaml_struct => MyStruct {
203/// int_field: OCamlInt,
204/// string_field: String,
205/// }
206/// };
207/// // ...
208/// # ()
209/// # }
210/// ```
211#[macro_export]
212macro_rules! ocaml_unpack_record {
213 ($var:ident => $cons:ident {
214 $($field:ident : $ocaml_typ:ty),+ $(,)?
215 }) => {{
216 let record = $var;
217 unsafe {
218 let mut current = 0;
219
220 $(
221 let $field = record.field::<$ocaml_typ>(current).to_rust();
222 current += 1;
223 )+
224
225 $cons {
226 $($field),+
227 }
228 }
229 }};
230
231 ($var:ident => $cons:ident (
232 $($field:ident : $ocaml_typ:ty),+ $(,)?
233 )) => {{
234 let record = $var;
235 unsafe {
236 let mut current = 0;
237
238 $(
239 let $field = record.field::<$ocaml_typ>(current).to_rust();
240 current += 1;
241 )+
242
243 $cons (
244 $($field),+
245 )
246 }
247 }};
248}
249
250/// Allocates an OCaml memory block tagged with the specified value.
251///
252/// It is used internally to allocate OCaml variants, its direct use is
253/// not recommended.
254#[macro_export]
255macro_rules! ocaml_alloc_tagged_block {
256 ($cr:ident, $tag:expr, $($field:ident : $ocaml_typ:ty),+ $(,)?) => {
257 unsafe {
258 let mut current = 0;
259 let field_count = $crate::count_fields!($($field)*);
260 let block: $crate::BoxRoot<()> = $crate::BoxRoot::new($crate::OCaml::new($cr, $crate::internal::caml_alloc(field_count, $tag)));
261 $(
262 let $field: $crate::OCaml<$ocaml_typ> = $field.to_ocaml($cr);
263 $crate::internal::store_field(block.get_raw(), current, $field.raw());
264 current += 1;
265 )+
266 $crate::OCaml::new($cr, block.get_raw())
267 }
268 };
269}
270
271/// Allocates an OCaml record built from a Rust record
272///
273/// Most of the time the [`impl_to_ocaml_record!`] macro will be used to define how records
274/// should be converted. This macro is useful when implementing OCaml allocation
275/// functions directly.
276///
277/// It is important that the order of the fields remains the same as in the OCaml type declaration.
278///
279/// # Examples
280///
281/// ```
282/// # use ocaml_interop::*;
283/// struct MyStruct {
284/// int_field: u8,
285/// string_field: String,
286/// }
287///
288/// // Assuming an OCaml record declaration like:
289/// //
290/// // type my_struct = {
291/// // int_field: int;
292/// // string_field: string;
293/// // }
294/// //
295/// // NOTE: What is important is the order of the fields, not their names.
296///
297/// # fn alloc_record_example(cr: &mut OCamlRuntime) {
298/// let ms = MyStruct { int_field: 132, string_field: "blah".to_owned() };
299/// let ocaml_ms: OCaml<MyStruct> = ocaml_alloc_record! {
300/// // value { field: OCamlType, ... }
301/// cr, ms { // cr: &mut OCamlRuntime
302/// // optionally `=> expr` can be used to pre-process the field value
303/// // before the conversion into OCaml takes place.
304/// // Inside the expression, a variable with the same name as the field
305/// // is bound to a reference to the field value.
306/// int_field: OCamlInt => { *int_field as i64 },
307/// string_field: String,
308/// }
309/// };
310/// // ...
311/// # ()
312/// # }
313/// ```
314#[macro_export]
315macro_rules! ocaml_alloc_record {
316 ($cr:ident, $self:ident {
317 $($field:ident : $ocaml_typ:ty $(=> $conv_expr:expr)?),+ $(,)?
318 }) => {
319 unsafe {
320 let mut current = 0;
321 let field_count = $crate::count_fields!($($field)*);
322 let record: $crate::BoxRoot<()> = $crate::BoxRoot::new($crate::OCaml::new($cr, $crate::internal::caml_alloc(field_count, 0)));
323 $(
324 let $field = &$crate::prepare_field_for_mapping!($self.$field $(=> $conv_expr)?);
325 let $field: $crate::OCaml<$ocaml_typ> = $field.to_ocaml($cr);
326 $crate::internal::store_field(record.get_raw(), current, $field.raw());
327 current += 1;
328 )+
329 $crate::OCaml::new($cr, record.get_raw())
330 }
331 };
332}
333
334/// Implements [`FromOCaml`] for mapping an OCaml record into a Rust record.
335///
336/// It is important that the order of the fields remains the same as in the OCaml type declaration.
337///
338/// # Examples
339///
340/// ```
341/// # use ocaml_interop::*;
342/// # ocaml! { fn make_mystruct(unit: ()) -> MyStruct; }
343/// struct MyStruct {
344/// int_field: i64,
345/// string_field: String,
346/// }
347///
348/// // Assuming an OCaml record declaration like:
349/// //
350/// // type my_struct = {
351/// // int_field: int;
352/// // string_field: string;
353/// // }
354/// //
355/// // NOTE: What is important is the order of the fields, not their names.
356///
357/// impl_from_ocaml_record! {
358/// // Optionally, if Rust and OCaml types don't match:
359/// // OCamlType => RustType { ... }
360/// MyStruct {
361/// int_field: OCamlInt,
362/// string_field: String,
363/// }
364/// }
365/// ```
366#[macro_export]
367macro_rules! impl_from_ocaml_record {
368 ($ocaml_typ:ident => $rust_typ:ident {
369 $($field:ident : $ocaml_field_typ:ty),+ $(,)?
370 }) => {
371 unsafe impl $crate::FromOCaml<$ocaml_typ> for $rust_typ {
372 fn from_ocaml(v: $crate::OCaml<$ocaml_typ>) -> Self {
373 $crate::ocaml_unpack_record! { v =>
374 $rust_typ {
375 $($field : $ocaml_field_typ),+
376 }
377 }
378 }
379 }
380 };
381
382 ($both_typ:ident {
383 $($t:tt)*
384 }) => {
385 $crate::impl_from_ocaml_record! {
386 $both_typ => $both_typ {
387 $($t)*
388 }
389 }
390 };
391
392 ($ocaml_typ:ident => $rust_typ:ident (
393 $($field:ident : $ocaml_field_typ:ty),+ $(,)?
394 )) => {
395 unsafe impl $crate::FromOCaml<$ocaml_typ> for $rust_typ {
396 fn from_ocaml(v: $crate::OCaml<$ocaml_typ>) -> Self {
397 $crate::ocaml_unpack_record! { v =>
398 $rust_typ (
399 $($field : $ocaml_field_typ),+
400 )
401 }
402 }
403 }
404 };
405
406 ($both_typ:ident (
407 $($t:tt)*
408 )) => {
409 $crate::impl_from_ocaml_record! {
410 $both_typ => $both_typ (
411 $($t)*
412 )
413 }
414 };
415}
416
417/// Implements [`ToOCaml`] for mapping a Rust record into an OCaml record.
418///
419/// It is important that the order of the fields remains the same as in the OCaml type declaration.
420///
421/// # Examples
422///
423/// ```
424/// # use ocaml_interop::*;
425/// struct MyStruct {
426/// int_field: u8,
427/// string_field: String,
428/// }
429///
430/// // Assuming an OCaml record declaration like:
431/// //
432/// // type my_struct = {
433/// // int_field: int;
434/// // string_field: string;
435/// // }
436/// //
437/// // NOTE: What is important is the order of the fields, not their names.
438///
439/// impl_to_ocaml_record! {
440/// // Optionally, if Rust and OCaml types don't match:
441/// // RustType => OCamlType { ... }
442/// MyStruct {
443/// // optionally `=> expr` can be used to preprocess the field value
444/// // before the conversion into OCaml takes place.
445/// // Inside the expression, a variable with the same name as the field
446/// // is bound to a reference to the field value.
447/// int_field: OCamlInt => { *int_field as i64 },
448/// string_field: String,
449/// }
450/// }
451/// ```
452#[macro_export]
453macro_rules! impl_to_ocaml_record {
454 ($rust_typ:ty => $ocaml_typ:ident {
455 $($field:ident : $ocaml_field_typ:ty $(=> $conv_expr:expr)?),+ $(,)?
456 }) => {
457 unsafe impl $crate::ToOCaml<$ocaml_typ> for $rust_typ {
458 fn to_ocaml<'a>(&self, cr: &'a mut $crate::OCamlRuntime) -> $crate::OCaml<'a, $ocaml_typ> {
459 $crate::ocaml_alloc_record! {
460 cr, self {
461 $($field : $ocaml_field_typ $(=> $conv_expr)?),+
462 }
463 }
464 }
465 }
466 };
467
468 ($both_typ:ident {
469 $($t:tt)*
470 }) => {
471 $crate::impl_to_ocaml_record! {
472 $both_typ => $both_typ {
473 $($t)*
474 }
475 }
476 };
477}
478
479/// Implements [`FromOCaml`] for mapping an OCaml variant into a Rust enum.
480///
481/// It is important that the order of the fields remains the same as in the OCaml type declaration.
482///
483/// # Examples
484///
485/// ```
486/// # use ocaml_interop::*;
487/// enum Movement {
488/// StepLeft,
489/// StepRight,
490/// Rotate(f64),
491/// }
492///
493/// // Assuming an OCaml type declaration like:
494/// //
495/// // type movement =
496/// // | StepLeft
497/// // | StepRight
498/// // | Rotate of float
499/// //
500/// // NOTE: What is important is the order of the tags, not their names.
501///
502/// impl_from_ocaml_variant! {
503/// // Optionally, if Rust and OCaml types don't match:
504/// // OCamlType => RustType { ... }
505/// Movement {
506/// // Alternative: StepLeft => Movement::StepLeft
507/// // <anyname> => <build-expr>
508/// Movement::StepLeft,
509/// Movement::StepRight,
510/// // Tag field names are mandatory
511/// Movement::Rotate(rotation: OCamlFloat),
512/// }
513/// }
514/// ```
515#[macro_export]
516macro_rules! impl_from_ocaml_variant {
517 ($ocaml_typ:ty => $rust_typ:ty {
518 $($t:tt)*
519 }) => {
520 unsafe impl $crate::FromOCaml<$ocaml_typ> for $rust_typ {
521 fn from_ocaml(v: $crate::OCaml<$ocaml_typ>) -> Self {
522 let result = $crate::ocaml_unpack_variant! {
523 v => {
524 $($t)*
525 }
526 };
527
528 let msg = concat!(
529 "Failure when unpacking an OCaml<", stringify!($ocaml_typ), "> variant into ",
530 stringify!($rust_typ), " (unexpected tag value)");
531
532 result.expect(msg)
533 }
534 }
535 };
536
537 ($both_typ:ty {
538 $($t:tt)*
539 }) => {
540 $crate::impl_from_ocaml_variant!{
541 $both_typ => $both_typ {
542 $($t)*
543 }
544 }
545 };
546}
547
548/// Unpacks an OCaml variant and maps it into a Rust enum.
549///
550/// This macro works on [`OCaml`]`<'gc, T>` values.
551///
552/// It is important that the order of the fields remains the same as in the OCaml type declaration.
553///
554/// # Note
555///
556/// Unlike with [`ocaml_unpack_record!`], the result of [`ocaml_unpack_variant!`] is a `Result` value.
557/// An error will be returned in the case of an unexpected tag value. This may change in the future.
558///
559/// # Examples
560///
561/// ```
562/// # use ocaml_interop::*;
563/// # ocaml! { fn make_ocaml_movement(unit: ()) -> Movement; }
564/// enum Movement {
565/// StepLeft,
566/// StepRight,
567/// Rotate(f64),
568/// }
569///
570/// // Assuming an OCaml type declaration like:
571/// //
572/// // type movement =
573/// // | StepLeft
574/// // | StepRight
575/// // | Rotate of float
576/// //
577/// // NOTE: What is important is the order of the tags, not their names.
578///
579/// # fn unpack_variant_example(cr: &mut OCamlRuntime) {
580/// let ocaml_variant_root = make_ocaml_movement(cr, &OCaml::unit());
581/// let ocaml_variant = cr.get(&ocaml_variant_root);
582/// let result = ocaml_unpack_variant! {
583/// ocaml_variant => {
584/// // Alternative: StepLeft => Movement::StepLeft
585/// // <anyname> => <build-expr>
586/// Movement::StepLeft,
587/// Movement::StepRight,
588/// // Tag field names are mandatory
589/// Movement::Rotate(rotation: OCamlFloat),
590/// }
591/// }.unwrap();
592/// // ...
593/// # }
594#[macro_export]
595macro_rules! ocaml_unpack_variant {
596 ($self:ident => {
597 $($($tag:ident)::+ $(($($slot_name:ident: $slot_typ:ty),+ $(,)?))? $(=> $conv:expr)?),+ $(,)?
598 }) => {
599 (|| {
600 let mut current_block_tag = 0;
601 let mut current_long_tag = 0;
602
603 $(
604 $crate::unpack_variant_tag!(
605 $self, current_block_tag, current_long_tag,
606 $($tag)::+ $(($($slot_name: $slot_typ),+))? $(=> $conv)?);
607 )+
608
609 Err("Invalid tag value found when converting from an OCaml variant")
610 })()
611 };
612
613 ($self:ident => {
614 $($($tag:ident)::+ $({$($slot_name:ident: $slot_typ:ty),+ $(,)?})? $(=> $conv:expr)?),+ $(,)?
615 }) => {
616 (|| {
617 let mut current_block_tag = 0;
618 let mut current_long_tag = 0;
619
620 $(
621 $crate::unpack_variant_tag!(
622 $self, current_block_tag, current_long_tag,
623 $($tag)::+ $({$($slot_name: $slot_typ),+})? $(=> $conv)?);
624 )+
625
626 Err("Invalid tag value found when converting from an OCaml variant")
627 })()
628 };
629}
630
631/// Allocates an OCaml variant, mapped from a Rust enum.
632///
633/// The match in this conversion is exhaustive, and requires that every enum case is covered.
634///
635/// It is important that the order of the fields remains the same as in the OCaml type declaration.
636///
637/// # Examples
638///
639/// ```
640/// # use ocaml_interop::*;
641/// # ocaml! { fn make_ocaml_movement(unit: ()) -> Movement; }
642/// enum Movement {
643/// StepLeft,
644/// StepRight,
645/// Rotate(f64),
646/// }
647///
648/// // Assuming an OCaml type declaration like:
649/// //
650/// // type movement =
651/// // | StepLeft
652/// // | StepRight
653/// // | Rotate of float
654/// //
655/// // NOTE: What is important is the order of the tags, not their names.
656///
657/// # fn alloc_variant_example(cr: &mut OCamlRuntime) {
658/// let movement = Movement::Rotate(180.0);
659/// let ocaml_movement: OCaml<Movement> = ocaml_alloc_variant! {
660/// cr, movement => {
661/// Movement::StepLeft,
662/// Movement::StepRight,
663/// // Tag field names are mandatory
664/// Movement::Rotate(rotation: OCamlFloat),
665/// }
666/// };
667/// // ...
668/// # }
669/// ```
670#[macro_export]
671macro_rules! ocaml_alloc_variant {
672 ($cr:ident, $self:ident => {
673 $($($tag:ident)::+ $(($($slot_name:ident: $slot_typ:ty),+ $(,)?))? $(,)?),+
674 }) => {
675 $crate::ocaml_alloc_variant_match!{
676 $cr, $self, 0u8, 0u8,
677
678 @units {}
679 @blocks {}
680
681 @pending $({ $($tag)::+ $(($($slot_name: $slot_typ),+))? })+
682 }
683 };
684}
685
686/// Implements [`ToOCaml`] for mapping a Rust enum into an OCaml variant.
687///
688/// The match in this conversion is exhaustive, and requires that every enum case is covered.
689///
690/// It is important that the order of the fields remains the same as in the OCaml type declaration.
691///
692/// # Examples
693///
694/// ```
695/// # use ocaml_interop::*;
696/// enum Movement {
697/// StepLeft,
698/// StepRight,
699/// Rotate(f64),
700/// }
701///
702/// // Assuming an OCaml type declaration like:
703/// //
704/// // type movement =
705/// // | StepLeft
706/// // | StepRight
707/// // | Rotate of float
708/// //
709/// // NOTE: What is important is the order of the tags, not their names.
710///
711/// impl_to_ocaml_variant! {
712/// // Optionally, if Rust and OCaml types don't match:
713/// // RustType => OCamlType { ... }
714/// Movement {
715/// Movement::StepLeft,
716/// Movement::StepRight,
717/// // Tag field names are mandatory
718/// Movement::Rotate(rotation: OCamlFloat),
719/// }
720/// }
721/// ```
722#[macro_export]
723macro_rules! impl_to_ocaml_variant {
724 ($rust_typ:ty => $ocaml_typ:ty {
725 $($t:tt)*
726 }) => {
727 unsafe impl $crate::ToOCaml<$ocaml_typ> for $rust_typ {
728 fn to_ocaml<'a>(&self, cr: &'a mut $crate::OCamlRuntime) -> $crate::OCaml<'a, $ocaml_typ> {
729 $crate::ocaml_alloc_variant! {
730 cr, self => {
731 $($t)*
732 }
733 }
734 }
735 }
736 };
737
738 ($both_typ:ty {
739 $($t:tt)*
740 }) => {
741 $crate::impl_to_ocaml_variant!{
742 $both_typ => $both_typ {
743 $($t)*
744 }
745 }
746 };
747}
748
749/// Implements [`ToOCaml`] for mapping a Rust enum into an OCaml polymorphic variant.
750///
751/// The match in this conversion is exhaustive, and requires that every enum case is covered.
752///
753/// Although the order of the tags doesn't matter, the Rust and OCaml names must match exactly.
754/// For tags containing multiple values, it is important that the order of the fields remains the same
755/// as in the OCaml type declaration.
756///
757/// # Examples
758///
759/// ```
760/// # use ocaml_interop::*;
761/// enum Movement {
762/// StepLeft,
763/// StepRight,
764/// Rotate(f64),
765/// }
766///
767/// // Assuming an OCaml type declaration like:
768/// //
769/// // type movement = [
770/// // | `StepLeft
771/// // | `StepRight
772/// // | `Rotate of float
773/// // ]
774/// //
775/// // NOTE: Order of tags is irrelevant but names must match exactly.
776///
777/// impl_to_ocaml_polymorphic_variant! {
778/// // Optionally, if Rust and OCaml types don't match:
779/// // RustType => OCamlType { ... }
780/// Movement {
781/// Movement::StepLeft,
782/// Movement::StepRight,
783/// // Tag field names are mandatory
784/// Movement::Rotate(rotation: OCamlFloat),
785/// }
786/// }
787/// ```
788#[macro_export]
789macro_rules! impl_to_ocaml_polymorphic_variant {
790 ($rust_typ:ty => $ocaml_typ:ty {
791 $($t:tt)*
792 }) => {
793 unsafe impl $crate::ToOCaml<$ocaml_typ> for $rust_typ {
794 fn to_ocaml<'a>(&self, cr: &'a mut $crate::OCamlRuntime) -> $crate::OCaml<'a, $ocaml_typ> {
795 $crate::ocaml_alloc_polymorphic_variant! {
796 cr, self => {
797 $($t)*
798 }
799 }
800 }
801 }
802 };
803
804 ($both_typ:ty {
805 $($t:tt)*
806 }) => {
807 $crate::impl_to_ocaml_polymorphic_variant!{
808 $both_typ => $both_typ {
809 $($t)*
810 }
811 }
812 };
813}
814
815/// Implements [`FromOCaml`] for mapping an OCaml polymorphic variant into a Rust enum.
816///
817/// Although the order of the tags doesn't matter, the Rust and OCaml names must match exactly.
818/// For tags containing multiple values, it is important that the order of the fields remains the same
819/// as in the OCaml type declaration.
820///
821/// # Examples
822///
823/// ```
824/// # use ocaml_interop::*;
825/// enum Movement {
826/// StepLeft,
827/// StepRight,
828/// Rotate(f64),
829/// }
830///
831/// // Assuming an OCaml type declaration like:
832/// //
833/// // type movement = [
834/// // | `StepLeft
835/// // | `StepRight
836/// // | `Rotate of float
837/// // ]
838///
839/// impl_from_ocaml_polymorphic_variant! {
840/// // Optionally, if Rust and OCaml types don't match:
841/// // OCamlType => RustType { ... }
842/// Movement {
843/// StepLeft => Movement::StepLeft,
844/// StepRight => Movement::StepRight,
845/// // Tag field names are mandatory
846/// Rotate(rotation: OCamlFloat)
847/// => Movement::Rotate(rotation),
848/// }
849/// }
850/// ```
851#[macro_export]
852macro_rules! impl_from_ocaml_polymorphic_variant {
853 ($ocaml_typ:ty => $rust_typ:ty {
854 $($t:tt)*
855 }) => {
856 unsafe impl $crate::FromOCaml<$ocaml_typ> for $rust_typ {
857 fn from_ocaml(v: $crate::OCaml<$ocaml_typ>) -> Self {
858 let result = $crate::ocaml_unpack_polymorphic_variant! {
859 v => {
860 $($t)*
861 }
862 };
863
864 let msg = concat!(
865 "Failure when unpacking an OCaml<", stringify!($ocaml_typ), "> polymorphic variant into ",
866 stringify!($rust_typ), " (unexpected tag value)");
867
868 result.expect(msg)
869 }
870 }
871 };
872
873 ($both_typ:ty {
874 $($t:tt)*
875 }) => {
876 $crate::impl_from_ocaml_polymorphic_variant!{
877 $both_typ => $both_typ {
878 $($t)*
879 }
880 }
881 };
882}
883
884/// Unpacks an OCaml polymorphic variant and maps it into a Rust enum.
885///
886/// # Note
887///
888/// Unlike with [`ocaml_unpack_record!`], the result of [`ocaml_unpack_polymorphic_variant!`] is a `Result` value.
889/// An error will be returned in the case of an unexpected tag value. This may change in the future.
890///
891/// # Examples
892///
893/// ```
894/// # use ocaml_interop::*;
895/// # ocaml! { fn make_ocaml_polymorphic_movement(unit: ()) -> Movement; }
896/// enum Movement {
897/// StepLeft,
898/// StepRight,
899/// Rotate(f64),
900/// }
901///
902/// // Assuming an OCaml type declaration like:
903/// //
904/// // type movement = [
905/// // | `StepLeft
906/// // | `StepRight
907/// // | `Rotate of float
908/// // ]
909///
910/// # fn unpack_polymorphic_variant_example(cr: &mut OCamlRuntime) {
911/// let ocaml_polymorphic_variant_root = make_ocaml_polymorphic_movement(cr, &OCaml::unit());
912/// let ocaml_polymorphic_variant = cr.get(&ocaml_polymorphic_variant_root);
913/// let result = ocaml_unpack_polymorphic_variant! {
914/// ocaml_polymorphic_variant => {
915/// StepLeft => Movement::StepLeft,
916/// StepRight => Movement::StepRight,
917/// // Tag field names are mandatory
918/// Rotate(rotation: OCamlFloat)
919/// => Movement::Rotate(rotation),
920/// }
921/// }.unwrap();
922/// // ...
923/// # }
924#[macro_export]
925macro_rules! ocaml_unpack_polymorphic_variant {
926 ($self:ident => {
927 $($tag:ident $(($($slot_name:ident: $slot_typ:ty),+ $(,)?))? => $conv:expr),+ $(,)?
928 }) => {
929 (|| {
930 $(
931 $crate::unpack_polymorphic_variant_tag!(
932 $self, $tag $(($($slot_name: $slot_typ),+))? => $conv);
933 )+
934
935 Err("Invalid tag value found when converting from an OCaml polymorphic variant")
936 })()
937 };
938}
939
940/// Allocates an OCaml polymorphic variant, mapped from a Rust enum.
941///
942/// The match in this conversion is exhaustive, and requires that every enum case is covered.
943///
944/// Although the order of the tags doesn't matter, the Rust and OCaml names must match exactly.
945/// For tags containing multiple values, it is important that the order of the fields remains the same
946/// as in the OCaml type declaration.
947///
948/// # Examples
949///
950/// ```
951/// # use ocaml_interop::*;
952/// # ocaml! { fn make_ocaml_movement(unit: ()) -> Movement; }
953/// enum Movement {
954/// StepLeft,
955/// StepRight,
956/// Rotate(f64),
957/// }
958///
959/// // Assuming an OCaml type declaration like:
960/// //
961/// // type movement = [
962/// // | `StepLeft
963/// // | `StepRight
964/// // | `Rotate of float
965/// // ]
966/// //
967/// // NOTE: Order of tags is irrelevant but names must match exactly.
968///
969/// # fn alloc_variant_example(cr: &mut OCamlRuntime) {
970/// let movement = Movement::Rotate(180.0);
971/// let ocaml_movement: OCaml<Movement> = ocaml_alloc_polymorphic_variant! {
972/// cr, movement => {
973/// Movement::StepLeft,
974/// Movement::StepRight,
975/// // Tag field names are mandatory
976/// Movement::Rotate(rotation: OCamlFloat),
977/// }
978/// };
979/// // ...
980/// # }
981/// ```
982#[macro_export]
983macro_rules! ocaml_alloc_polymorphic_variant {
984 ($cr:ident, $self:ident => {
985 $($($tag:ident)::+ $(($($slot_name:ident: $slot_typ:ty),+ $(,)?))? $(,)?),+
986 }) => {
987 $crate::ocaml_alloc_polymorphic_variant_match!{
988 $cr, $self,
989
990 @units {}
991 @unit_blocks {}
992 @blocks {}
993
994 @pending $({ $($tag)::+ $(($($slot_name: $slot_typ),+))? })+
995 }
996 };
997}
998
999// Internal utility macros
1000
1001#[doc(hidden)]
1002#[macro_export]
1003macro_rules! count_fields {
1004 () => {0usize};
1005 ($_f1:ident $_f2:ident $_f3:ident $_f4:ident $_f5:ident $($fields:ident)*) => {
1006 5usize + $crate::count_fields!($($fields)*)
1007 };
1008 ($field:ident $($fields:ident)*) => {1usize + $crate::count_fields!($($fields)*)};
1009}
1010
1011#[doc(hidden)]
1012#[macro_export]
1013macro_rules! prepare_field_for_mapping {
1014 ($self:ident.$field:ident) => {
1015 $self.$field
1016 };
1017
1018 ($self:ident.$field:ident => $conv_expr:expr) => {{
1019 let $field = &$self.$field;
1020 $conv_expr
1021 }};
1022}
1023
1024// TODO: check generated machine code and see if it is worth it to generate a switch
1025#[doc(hidden)]
1026#[macro_export]
1027macro_rules! unpack_variant_tag {
1028 ($self:ident, $current_block_tag:ident, $current_long_tag:ident, $($tag:ident)::+) => {
1029 $crate::unpack_variant_tag!($self, $current_block_tag, $current_long_tag, $($tag)::+ => $($tag)::+)
1030 };
1031
1032 ($self:ident, $current_block_tag:ident, $current_long_tag:ident, $($tag:ident)::+ => $conv:expr) => {
1033 if $self.is_long() && $crate::internal::int_val(unsafe { $self.raw() }) == $current_long_tag {
1034 return Ok($conv);
1035 }
1036 $current_long_tag += 1;
1037 };
1038
1039 // Parens: tuple
1040 ($self:ident, $current_block_tag:ident, $current_long_tag:ident,
1041 $($tag:ident)::+ ($($slot_name:ident: $slot_typ:ty),+)) => {
1042
1043 $crate::unpack_variant_tag!(
1044 $self, $current_block_tag, $current_long_tag,
1045 $($tag)::+ ($($slot_name: $slot_typ),+) => $($tag)::+($($slot_name),+))
1046 };
1047
1048 // Braces: record
1049 ($self:ident, $current_block_tag:ident, $current_long_tag:ident,
1050 $($tag:ident)::+ {$($slot_name:ident: $slot_typ:ty),+}) => {
1051
1052 $crate::unpack_variant_tag!(
1053 $self, $current_block_tag, $current_long_tag,
1054 $($tag)::+ {$($slot_name: $slot_typ),+} => $($tag)::+{$($slot_name),+})
1055 };
1056
1057 // Parens: tuple
1058 ($self:ident, $current_block_tag:ident, $current_long_tag:ident,
1059 $($tag:ident)::+ ($($slot_name:ident: $slot_typ:ty),+) => $conv:expr) => {
1060
1061 if $self.is_block() && $self.tag_value() == $current_block_tag {
1062 let mut current_field = 0;
1063
1064 $(
1065 let $slot_name = unsafe { $self.field::<$slot_typ>(current_field).to_rust() };
1066 current_field += 1;
1067 )+
1068
1069 return Ok($conv);
1070 }
1071 $current_block_tag += 1;
1072 };
1073
1074 // Braces: record
1075 ($self:ident, $current_block_tag:ident, $current_long_tag:ident,
1076 $($tag:ident)::+ {$($slot_name:ident: $slot_typ:ty),+} => $conv:expr) => {
1077
1078 if $self.is_block() && $self.tag_value() == $current_block_tag {
1079 let mut current_field = 0;
1080
1081 $(
1082 let $slot_name = unsafe { $self.field::<$slot_typ>(current_field).to_rust() };
1083 current_field += 1;
1084 )+
1085
1086 return Ok($conv);
1087 }
1088 $current_block_tag += 1;
1089 };
1090}
1091
1092#[doc(hidden)]
1093#[macro_export]
1094macro_rules! ocaml_alloc_variant_match {
1095 // Base case, generate `match` expression
1096 ($cr:ident, $self:ident, $current_block_tag:expr, $current_long_tag:expr,
1097
1098 @units {
1099 $({ $($unit_tag:ident)::+ @ $unit_tag_counter:expr })*
1100 }
1101 @blocks {
1102 $({ $($block_tag:ident)::+ ($($block_slot_name:ident: $block_slot_typ:ty),+) @ $block_tag_counter:expr })*
1103 }
1104
1105 @pending
1106 ) => {
1107 match $self {
1108 $(
1109 $($unit_tag)::+ =>
1110 unsafe { $crate::OCaml::new($cr, $crate::OCaml::of_i64_unchecked($unit_tag_counter as i64).raw()) },
1111 )*
1112 $(
1113 $($block_tag)::+($($block_slot_name),+) =>
1114 $crate::ocaml_alloc_tagged_block!($cr, $block_tag_counter, $($block_slot_name: $block_slot_typ),+),
1115 )*
1116 }
1117 };
1118
1119 // Found unit tag, add to accumulator and increment unit variant tag number
1120 ($cr:ident, $self:ident, $current_block_tag:expr, $current_long_tag:expr,
1121
1122 @units { $($unit_tags_accum:tt)* }
1123 @blocks { $($block_tags_accum:tt)* }
1124
1125 @pending
1126 { $($found_tag:ident)::+ }
1127 $($tail:tt)*
1128 ) => {
1129 $crate::ocaml_alloc_variant_match!{
1130 $cr, $self, $current_block_tag, {1u8 + $current_long_tag},
1131
1132 @units {
1133 $($unit_tags_accum)*
1134 { $($found_tag)::+ @ $current_long_tag }
1135 }
1136 @blocks { $($block_tags_accum)* }
1137
1138 @pending $($tail)*
1139 }
1140 };
1141
1142 // Found block tag, add to accumulator and increment block variant tag number
1143 ($cr:ident, $self:ident, $current_block_tag:expr, $current_long_tag:expr,
1144
1145 @units { $($unit_tags_accum:tt)* }
1146 @blocks { $($block_tags_accum:tt)* }
1147
1148 @pending
1149 { $($found_tag:ident)::+ ($($found_slot_name:ident: $found_slot_typ:ty),+) }
1150 $($tail:tt)*
1151 ) => {
1152 $crate::ocaml_alloc_variant_match!{
1153 $cr, $self, {1u8 + $current_block_tag}, $current_long_tag,
1154
1155 @units { $($unit_tags_accum)* }
1156 @blocks {
1157 $($block_tags_accum)*
1158 { $($found_tag)::+ ($($found_slot_name: $found_slot_typ),+) @ $current_block_tag }
1159 }
1160
1161 @pending $($tail)*
1162 }
1163 };
1164}
1165
1166#[doc(hidden)]
1167#[macro_export]
1168macro_rules! ocaml_alloc_polymorphic_variant_match {
1169 // Base case, generate `match` expression
1170 ($cr:ident, $self:ident,
1171
1172 @units {
1173 $({ $($unit_tag:ident)::+ })*
1174 }
1175 @unit_blocks {
1176 $({ $($unit_block_tag:ident)::+ ($unit_block_slot_name:ident: $unit_block_slot_typ:ty) })*
1177 }
1178 @blocks {
1179 $({ $($block_tag:ident)::+ ($($block_slot_name:ident: $block_slot_typ:ty),+) })*
1180 }
1181
1182 @pending
1183 ) => {
1184 match &$self {
1185 $(
1186 $($unit_tag)::+ => {
1187 let polytag = $crate::polymorphic_variant_tag_hash!($($unit_tag)::+);
1188 unsafe { $crate::OCaml::new($cr, polytag) }
1189 },
1190 )*
1191 $(
1192 $($unit_block_tag)::+($unit_block_slot_name) => {
1193 let polytag = $crate::polymorphic_variant_tag_hash!($($unit_block_tag)::+);
1194 let $unit_block_slot_name: $crate::BoxRoot<$unit_block_slot_typ> =
1195 $crate::ToOCaml::to_boxroot($unit_block_slot_name, $cr);
1196 unsafe {
1197 let block = $crate::internal::caml_alloc(2, $crate::internal::tag::TAG_POLYMORPHIC_VARIANT);
1198 $crate::internal::store_field(block, 0, polytag);
1199 $crate::internal::store_field(block, 1, $unit_block_slot_name.get($cr).raw());
1200 $crate::OCaml::new($cr, block)
1201 }
1202 },
1203 )*
1204 $(
1205 $($block_tag)::+($($block_slot_name),+) => {
1206 let polytag = $crate::polymorphic_variant_tag_hash!($($block_tag)::+);
1207 let tuple: $crate::BoxRoot<($($block_slot_typ),+)> =
1208 $crate::BoxRoot::new(unsafe {
1209 $crate::internal::alloc_tuple($cr, $crate::count_fields!($($block_slot_name)+))
1210 });
1211 let mut n = 0;
1212 $(
1213 let $block_slot_name: $crate::OCaml<$block_slot_typ> =
1214 $crate::ToOCaml::to_ocaml($block_slot_name, $cr);
1215 let raw = unsafe { $block_slot_name.raw() };
1216 unsafe { $crate::internal::store_field(tuple.get($cr).raw(), n, raw) };
1217 n += 1;
1218 )+
1219 unsafe {
1220 let block = $crate::internal::caml_alloc(2, $crate::internal::tag::TAG_POLYMORPHIC_VARIANT);
1221 $crate::internal::store_field(block, 0, polytag);
1222 $crate::internal::store_field(block, 1, tuple.get($cr).raw());
1223 $crate::OCaml::new($cr, block)
1224 }
1225 },
1226 )*
1227 }
1228 };
1229
1230 // Found unit tag, add to accumulator
1231 ($cr:ident, $self:ident,
1232
1233 @units { $($unit_tags_accum:tt)* }
1234 @unit_blocks { $($unit_block_tags_accum:tt)* }
1235 @blocks { $($block_tags_accum:tt)* }
1236
1237 @pending
1238 { $($found_tag:ident)::+ }
1239 $($tail:tt)*
1240 ) => {
1241 $crate::ocaml_alloc_polymorphic_variant_match!{
1242 $cr, $self,
1243
1244 @units {
1245 $($unit_tags_accum)*
1246 { $($found_tag)::+ }
1247 }
1248 @unit_blocks { $($unit_block_tags_accum)* }
1249 @blocks { $($block_tags_accum)* }
1250
1251 @pending $($tail)*
1252 }
1253 };
1254
1255 // Found unit tag with non-block value, add to accumulator
1256 ($cr:ident, $self:ident,
1257
1258 @units { $($unit_tags_accum:tt)* }
1259 @unit_blocks { $($unit_block_tags_accum:tt)* }
1260 @blocks { $($block_tags_accum:tt)* }
1261
1262 @pending
1263 { $($found_tag:ident)::+ ($found_slot_name:ident: $found_slot_typ:ty) }
1264 $($tail:tt)*
1265 ) => {
1266 $crate::ocaml_alloc_polymorphic_variant_match!{
1267 $cr, $self,
1268
1269 @units { $($unit_tags_accum)* }
1270 @unit_blocks {
1271 $($unit_block_tags_accum)*
1272 { $($found_tag)::+ ($found_slot_name: $found_slot_typ) }
1273 }
1274 @blocks { $($block_tags_accum)* }
1275
1276 @pending $($tail)*
1277 }
1278 };
1279
1280 // Found block tag with a block value, add to accumulator
1281 ($cr:ident, $self:ident,
1282
1283 @units { $($unit_tags_accum:tt)* }
1284 @unit_blocks { $($unit_block_tags_accum:tt)* }
1285 @blocks { $($block_tags_accum:tt)* }
1286
1287 @pending
1288 { $($found_tag:ident)::+ ($($found_slot_name:ident: $found_slot_typ:ty),+) }
1289 $($tail:tt)*
1290 ) => {
1291 $crate::ocaml_alloc_polymorphic_variant_match!{
1292 $cr, $self,
1293
1294 @units { $($unit_tags_accum)* }
1295 @unit_blocks { $($unit_block_tags_accum)* }
1296 @blocks {
1297 $($block_tags_accum)*
1298 { $($found_tag)::+ ($($found_slot_name: $found_slot_typ),+) }
1299 }
1300
1301 @pending $($tail)*
1302 }
1303 };
1304}
1305
1306// TODO: check generated machine code and see if it is worth it to generate a switch
1307#[doc(hidden)]
1308#[macro_export]
1309macro_rules! unpack_polymorphic_variant_tag {
1310 ($self:ident, $tag:ident => $conv:expr) => {
1311 #[allow(non_snake_case)]
1312 let $tag = $crate::polymorphic_variant_tag_hash!($tag);
1313 if $self.is_long() && unsafe { $self.raw() } == $tag {
1314 return Ok($conv);
1315 }
1316 };
1317
1318 ($self:ident, $tag:ident($slot_name:ident: $slot_typ:ty) => $conv:expr) => {
1319 #[allow(non_snake_case)]
1320 let $tag = $crate::polymorphic_variant_tag_hash!($tag);
1321
1322 if $self.is_block_sized(2) &&
1323 $self.tag_value() == $crate::internal::tag::TAG_POLYMORPHIC_VARIANT &&
1324 unsafe { $self.field::<$crate::OCamlInt>(0).raw() } == $tag {
1325
1326 let $slot_name = unsafe { $self.field::<$slot_typ>(1).to_rust() };
1327
1328 return Ok($conv);
1329 }
1330 };
1331
1332 ($self:ident, $tag:ident($($slot_name:ident: $slot_typ:ty),+) => $conv:expr) => {
1333 #[allow(non_snake_case)]
1334 let $tag = $crate::polymorphic_variant_tag_hash!($tag);
1335
1336 if $self.is_block_sized(2) &&
1337 $self.tag_value() == $crate::internal::tag::TAG_POLYMORPHIC_VARIANT &&
1338 unsafe { $self.field::<$crate::OCamlInt>(0).raw() } == $tag {
1339
1340 let ($($slot_name),+) = unsafe { $self.field::<($($slot_typ),+)>(1).to_rust() };
1341
1342 return Ok($conv);
1343 }
1344 };
1345}
1346
1347#[doc(hidden)]
1348#[macro_export]
1349macro_rules! ocaml_closure_reference {
1350 ($var:ident, $name:ident) => {
1351 static NAME: &str = stringify!($name);
1352 static mut OC: Option<$crate::internal::OCamlClosure> = None;
1353 static INIT: ::std::sync::Once = ::std::sync::Once::new();
1354 let $var = unsafe {
1355 INIT.call_once(|| {
1356 OC = $crate::internal::OCamlClosure::named(NAME);
1357 });
1358 OC.unwrap_or_else(|| panic!("OCaml closure with name '{}' not registered", NAME))
1359 };
1360 };
1361}
1362
1363#[doc(hidden)]
1364#[macro_export]
1365macro_rules! default_to_unit {
1366 // No return value, default to unit
1367 () => {
1368 ()
1369 };
1370
1371 // Return value specified
1372 ($rtyp:ty) => {
1373 $rtyp
1374 };
1375}
1376
1377#[doc(hidden)]
1378#[macro_export]
1379macro_rules! polymorphic_variant_tag_hash {
1380 // For Path::To::Last we take just Last
1381 ($prefix:ident::$($tag:ident)::+) => {
1382 $crate::polymorphic_variant_tag_hash!($($tag)::+)
1383 };
1384
1385 ($tag:ident) => {{
1386 static mut TAG_HASH: $crate::RawOCaml = 0;
1387 static INIT_TAG_HASH: std::sync::Once = std::sync::Once::new();
1388 unsafe {
1389 INIT_TAG_HASH.call_once(|| {
1390 TAG_HASH =
1391 $crate::internal::caml_hash_variant(concat!(stringify!($tag), "\0").as_ptr())
1392 });
1393 TAG_HASH
1394 }
1395 }};
1396}