boing_internals/
lib.rs

1// SPDX-License-Identifier: MPL-2.0
2
3use darling::FromDeriveInput as _;
4use proc_macro::TokenStream;
5
6mod bind_callback;
7mod bind_push_child;
8mod bind_set_child;
9mod bind_text;
10mod container;
11mod subcontrol;
12mod widget;
13
14macro_rules! attribute_macro {
15    ($name:ident $ty:ty) => {
16        #[proc_macro_attribute]
17        pub fn $name(attr: TokenStream, item: TokenStream) -> TokenStream {
18            let attr = syn::parse_macro_input!(attr as syn::AttributeArgs);
19
20            $name::expand(
21                darling::FromMeta::from_list(&attr).unwrap(),
22                syn::parse_macro_input!(item as $ty),
23            )
24        }
25    };
26}
27
28macro_rules! derive_macro {
29    ($name:ident $mod:ident $trait:ident) => {
30        #[proc_macro_derive($trait, attributes($mod))]
31        pub fn $name(input: TokenStream) -> TokenStream {
32            let input = syn::parse_macro_input!(input as syn::DeriveInput);
33            let attr = $mod::Attr::from_derive_input(&input).unwrap();
34
35            $mod::expand(attr, input)
36        }
37    };
38}
39
40attribute_macro!(bind_callback syn::ItemFn);
41attribute_macro!(bind_push_child syn::ForeignItemFn);
42attribute_macro!(bind_set_child syn::ForeignItemFn);
43attribute_macro!(bind_text syn::ForeignItemFn);
44attribute_macro!(subcontrol syn::ItemStruct);
45
46derive_macro!(derive_container container Container);
47derive_macro!(derive_widget widget Widget);
48
49mod prelude {
50    pub fn collect_attrs(
51        attrs: impl IntoIterator<Item = syn::Attribute>,
52    ) -> quote::__private::TokenStream {
53        // FIXME: I shouldn't need to reach into a private, implementation-detail module to obtain a
54        // type that [`quote`] uses in its public API. I could import *proc-macro2* myself, but
55        // there is no guarantee that it will be the same version that *quote* uses.
56        attrs.into_iter().map(quote::ToTokens::into_token_stream).collect()
57    }
58
59    pub fn extract_immut_recv(args: &mut impl Iterator<Item = syn::FnArg>) -> syn::Receiver {
60        args.next()
61            .and_then(|arg| match arg {
62                syn::FnArg::Receiver(it) => Some(it),
63                _ => None,
64            })
65            .filter(|arg| arg.mutability.is_none())
66            .expect("expected &self argument")
67    }
68
69    pub fn assert_no_extra_args(args: &mut impl Iterator<Item = syn::FnArg>) {
70        assert!(args.next().is_none(), "found extraneous arguments");
71    }
72
73    pub fn assert_ret_ty_is_inferred(ret_ty: &syn::ReturnType) {
74        let is_inferred = match ret_ty {
75            syn::ReturnType::Default => false,
76            syn::ReturnType::Type(_, ty) => matches!(**ty, syn::Type::Infer(_)),
77        };
78        assert!(is_inferred, "expected return type of _");
79    }
80
81    #[derive(darling::FromMeta)]
82    pub struct BindFnAttr {
83        #[darling(rename = "fn")]
84        pub libui_fn: syn::Ident,
85    }
86}