buffi_macro/
lib.rs

1use ::proc_macro::TokenStream;
2
3mod annotation;
4mod buffi_annotation_attributes;
5mod proc_macro;
6
7const FUNCTION_PREFIX: &str = "buffi";
8
9/// This macro generates a compatible c function for each function in the current impl block
10///
11/// The generated c function accepts arguments as bincode serialized byte buffers and returns
12/// a bincode serialized byte buffer as well. The generated function handles (de)serialization of
13/// those buffers internally and converts the arguments/results of each function internally. In
14/// addition the generated function contains code to handle panics before reaching the FFI boundary,
15/// blocking async funtcions and converting `color_eyre::Report` error types to a FFI compatible version
16///
17/// The generated c function will be named `buffi_{function_name}`. It accepts a pointer to the current
18/// type (`Self`) as first argument. For each other argument of the rust function, two arguments for
19/// the c function are generated: `{argument}` as `*const u8` pointing to the serialized argument
20/// and `{argument}_size` as `usize` containing a buffer size. In addition a `out_ptr: *mut *mut u8`
21/// argument is generated. This pointer will be set to the output buffer allocation. The generated function
22/// returns a `usize` indicating the size of the allocated buffer. This buffer needs to be freed
23/// via `buffi_free_byte_buffer`
24///
25/// In addition this macro prepends a `#[tracing::instrument]` attribute to each function
26/// in the current impl block
27///
28/// Modules containing a `#[buffi_macro::exported]` call needs to be public!
29#[proc_macro_attribute]
30pub fn exported(_att: TokenStream, item: TokenStream) -> TokenStream {
31    match syn::parse(item.clone()).and_then(|parsed_item| proc_macro::expand(parsed_item, None)) {
32        Ok(tokenstream) => tokenstream,
33        Err(e) => {
34            let mut out = proc_macro2::TokenStream::from(item);
35            out.extend(e.to_compile_error());
36            out
37        }
38    }
39    .into()
40}
41
42/// A helper derive to put annotations for the codegen on struct and enum fields
43///
44/// Available annotations are `#[buffi(skip)]` and `#[buffi(type = SomeType]`. See below
45/// how they can be utilized.
46///
47/// Putting this derive on a type that is available via a public API allows to modify
48/// availability of fields or which specific type should be used in the FFI if type
49/// mapping is not obvious.
50///
51/// Skipping a field
52///
53/// `#[buffi(skip)]` can be used together with `#[serde(skip)]` and `#[serde(default)]`
54/// to hide fields from the FFI. In that case when this type is used in the FFI it will
55/// appear on the Rust side with a default value set for that specific field.
56///
57/// Modifying the type mapping
58///
59/// `#[buffi(type = SomeType]` can be used in cases where the Rust type is not the desired type for
60/// the FFI. It applies either where the binary representation created by serde matches
61/// another type (e.g. for `url::Url` and `String` where you have `Url` on the Rust side
62/// and want `String` in your FFI) or where the application of custom (de-)serialization
63/// is required. This second use case is connected to `#[serde(serialize_with = ...]` and
64/// `#[serde(deserialize_with = ...]`. Both annotations have to be present and have to
65/// point to functions implemented on a (helper) struct that implements buffi's `SafeTypeMapping`
66/// trait.
67///
68/// Buffi already provides some implementations of `SafeTypeMapping`. Also check if some
69/// additional implementations can be utilized via crate features (e.g. `url2`).
70#[proc_macro_derive(Annotation, attributes(buffi, serde))]
71pub fn annotation(item: TokenStream) -> TokenStream {
72    syn::parse(item)
73        .and_then(annotation::expand)
74        .unwrap_or_else(|e| e.to_compile_error())
75        .into()
76}