1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#[allow(unused_imports)]
use paste::paste;

/// `ergo-lib` uses a number of collection types that are simple wrappers around `Vec`. We have a
/// generic type for such a collection in `ergo-lib-c-core::collections::Collection`. A limitation
/// of `cbindgen` is that it cannot process generic functions. This macro generates a C-compatible
/// interface for such a collection for any desired type T.
///
/// As an example the call `make_collection(BlockHeaders, BlockHeader);` generates:
///
///```
///pub type BlockHeadersPtr = CollectionPtr<BlockHeader>;
///pub type ConstBlockHeadersPtr = ConstCollectionPtr<BlockHeader>;
///
///#[no_mangle]
///pub unsafe extern "C" fn ergo_lib_block_headers_new(collection_ptr_out: *mut BlockHeadersPtr) {
///    #[allow(clippy::unwrap_used)]
///    collection_new(collection_ptr_out).unwrap();
///}
///
///#[no_mangle]
///pub unsafe extern "C" fn ergo_lib_block_headers_delete(collection_ptr_out: BlockHeadersPtr) {
///    collection_delete(collection_ptr_out)
///}
///
///#[no_mangle]
///pub unsafe extern "C" fn ergo_lib_block_headers_len(
///    collection_ptr: ConstBlockHeadersPtr,
///) -> usize {
///    #[allow(clippy::unwrap_used)]
///    collection_len(collection_ptr).unwrap()
///}
///
///#[no_mangle]
///pub unsafe extern "C" fn ergo_lib_block_headers_get(
///    collection_ptr: ConstBlockHeadersPtr,
///    index: usize,
///    element_ptr_out: *mut BlockHeaderPtr,
///) -> ReturnOption {
///    match collection_get(collection_ptr, index, element_ptr_out) {
///       Ok(is_some) => crate::ReturnOption {
///           is_some,
///           error: std::ptr::null_mut(),
///       },
///       Err(e) => crate::ReturnOption {
///           is_some: false, // Just a dummy value
///           error: Error::c_api_from(Err(e)),
///       },
///    }
///}
///
///#[no_mangle]
///pub unsafe extern "C" fn ergo_lib_block_headers_add(
///    element_ptr: ConstBlockHeaderPtr,
///    collection_ptr_out: BlockHeadersPtr,
///) {
///    #[allow(clippy::unwrap_used)]
///    collection_add(collection_ptr_out, element_ptr).unwrap();
///}
///```
#[macro_export]
macro_rules! make_collection {
    ($collection_type_name:ident, $item_type_name:ident) => {
        paste! {
            pub type [<$collection_type_name Ptr>] = ergo_lib_c_core::collections::CollectionPtr<$item_type_name>;
            pub type [<Const $collection_type_name Ptr>] = ergo_lib_c_core::collections::ConstCollectionPtr<$item_type_name>;

            /// Create a new empty collection
            #[no_mangle]
            pub unsafe extern "C" fn [<ergo_lib_ $collection_type_name:snake _new>](
                collection_ptr_out: *mut [<$collection_type_name Ptr>],
            ) {
                #[allow(clippy::unwrap_used)]
                ergo_lib_c_core::collections::collection_new(collection_ptr_out).unwrap();
            }

            /// Delete an existing collection
            #[no_mangle]
            pub unsafe extern "C" fn [<ergo_lib_ $collection_type_name:snake _delete>](ptr_out: [<$collection_type_name Ptr>]) {
                ergo_lib_c_core::collections::collection_delete(ptr_out)
            }

            /// Returns length of an existing collection
            #[no_mangle]
            pub unsafe extern "C" fn [<ergo_lib_ $collection_type_name:snake _len>](
                collection_ptr: [<Const $collection_type_name Ptr>],
            ) -> usize {
                #[allow(clippy::unwrap_used)]
                ergo_lib_c_core::collections::collection_len(collection_ptr).unwrap()
            }

            /// Returns element at position `index` of an existing collection
            #[no_mangle]
            pub unsafe extern "C" fn [<ergo_lib_ $collection_type_name:snake _get>](
                collection_ptr: [<Const $collection_type_name Ptr>],
                index: usize,
                element_ptr_out: *mut [<$item_type_name Ptr>],
            ) -> $crate::ReturnOption {
                match ergo_lib_c_core::collections::collection_get(collection_ptr, index, element_ptr_out) {
                    Ok(is_some) => $crate::ReturnOption {
                        is_some,
                        error: std::ptr::null_mut(),
                    },
                    Err(e) => $crate::ReturnOption {
                        is_some: false, // Just a dummy value
                        error: Error::c_api_from(Err(e)),
                    },
                }
            }

            #[no_mangle]
            /// Add an element to collection
            pub unsafe extern "C" fn [<ergo_lib_ $collection_type_name:snake _add>](
                element_ptr: [<Const $item_type_name Ptr>],
                collection_ptr_out: [<$collection_type_name Ptr>],
            ) {
                #[allow(clippy::unwrap_used)]
                ergo_lib_c_core::collections::collection_add(collection_ptr_out, element_ptr).unwrap();
            }

        }
    };
}

/// Generates an equality function for FFI
#[macro_export]
macro_rules! make_ffi_eq {
    ($type_name:ident) => {
        paste! {
            #[no_mangle]
            pub unsafe extern "C" fn [<ergo_lib_ $type_name:snake _eq>](
              [< $type_name:snake _ptr_0>]: [< Const $type_name Ptr>],
              [< $type_name:snake _ptr_1>]: [< Const $type_name Ptr>],
            ) -> bool {
              ergo_lib_c_core::util::deref_eq([< $type_name:snake _ptr_0>], [< $type_name:snake _ptr_1>])
            }
        }
    };
}