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}