ark_api_ffi/ffi/example_automatic.rs
1use crate::FFIResult;
2use bytemuck::CheckedBitPattern;
3use bytemuck::NoUninit;
4
5// Types used in the return position must implement `Default` when calling the
6// generated safe methods, as they are created on the module side before passing
7// a mutable pointer to them for the host to actually fill in.
8//
9// Note that you'll still need to derive `NoUninit` and `CheckedBitPattern` manually
10// on structs. The reason for this is that you may also want to derive `Pod` and `Zeroable`
11// instead, and there's currently no way of figuring out which to derive automatically.
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, NoUninit, CheckedBitPattern)]
13#[repr(C)]
14pub struct IsReady {
15 pub is_ready: bool,
16 // Again, more details on why this is necessary later, but basically, if this wasn't
17 // here then there would be padding bytes in this struct layout so that `size`
18 // could be aligned. We take those padding bytes up with "explicit"/"manual" padding
19 // here instead, so that we can satisfy the requirements of `NoUninit`
20 pub _pad: [u8; 3],
21 pub size: u32,
22}
23
24// The ark_bindgen macro automatically synthesizes proper FFI bindings from
25// more idiomatic Rust function signatures.
26#[ark_api_macros::ark_bindgen(imports = "example-automatic")]
27// The module name, in this case `example` is the "namespace" for the FFI
28// functions. This name will be used to prefix each function in the
29// `extern "C"` block that **must** be inside the module.
30mod example {
31 use super::*;
32
33 // Enums used as FFI parameters need to be moved into the procedural macro
34 // as the macro will extract the primitive type to convert to
35 // from the #[repr(primitive_type)]. This means that #[repr(primitive_type)]
36 // must be present or you'll get an error message.
37 // #[derive(IntoPrimitive, TryFromPrimitive, NoUninit, CheckedBitPattern)] are
38 // then generated for you on enums automatically.
39 #[derive(Copy, Clone, Debug)]
40 #[repr(u32)]
41 pub enum State {
42 SomeStateX,
43 SomeStateY,
44 }
45
46 extern "C" {
47 pub fn is_ready(
48 // We just pass a &str like normal, it will be converted
49 // into a pointer + length for you, the same for slices
50 name: &str,
51 // FFI safe types such as u32 are passed through transparently
52 max: u32,
53 // We just pass an enum like normal, it will be converted into
54 // its primitive type for you.
55 state: State,
56 // FFIResult<T> is a special type in ark_api_ffi that is recognized
57 // by the proc macro. It automatically converts the T into a *mut T
58 // as the last parameter, with `ErrorCode` as the actual return type
59 ) -> FFIResult<IsReady>;
60
61 // It's possible to return Vec of bytes (and only for this type!) from
62 // the host.
63 pub fn ret_byte_vector() -> FFIResult<Vec<u8>>;
64
65 // And also a string!
66 pub fn ret_string() -> FFIResult<String>;
67
68 // Return nothing in particular, but can still generate an error the user can react to.
69 pub fn works() -> FFIResult<()>;
70
71 // Returns a number, infallible mode. Note the host can still run into internal errors and
72 // abort execution of the Ark module. In this case, the Ark module developer can't do
73 // anything about it, so this function returns a plain type instead of the `FFIResult`
74 // type.
75 pub fn infallible_number(x: u32) -> u32;
76 }
77}
78
79/// Due to some limitations on how attribute macro currently work, our proc
80/// macro needs to be placed on an inner module. This means that other code
81/// using us has to have an unnecessary level of indirection to use the types/
82/// functions in that module, so instead we just re-export everything so they
83/// only need to access the single top level module to get everything
84pub use example::*;