Skip to main content

egui_reactor_macros/
lib.rs

1//! Procedural macros for egui-reactor: `rsx!`, `#[component]` and `#[hook]`.
2//!
3//! These are re-exported from `egui_reactor`, which is also the crate the
4//! expansions refer to: everything they emit is fully qualified as
5//! `::egui_reactor::..`, so user code only needs `use egui_reactor::prelude::*`.
6
7mod component;
8mod hook;
9mod rsx;
10mod util;
11
12use proc_macro::TokenStream;
13
14/// Turn a function into a egui-reactor component.
15///
16/// ```ignore
17/// #[component]
18/// fn Counter(cx: &mut Cx, initial: i32, label: Option<&str>, #[event] on_change: i32) {
19///     ..
20/// }
21/// ```
22///
23/// Generates a `CounterProps` struct with a typed builder, an optional
24/// `CounterEvent` enum (one variant per `#[event]` argument) and a
25/// `Counter(cx, props)` function. The body's tail expression is shown as a
26/// `View`.
27///
28/// Argument attributes:
29///
30/// - `#[prop(default)]` / `#[prop(default = expr)]`: optional prop.
31/// - `#[prop(into)]`: the setter takes `impl Into<T>`.
32/// - `#[event]`: becomes an `Emitter` in the body and a variant in the event enum.
33///
34/// An argument of type `Option<T>` is optional without any attribute. An
35/// argument named `children` receives the child nodes; if it is not declared,
36/// one of type `()` is generated, because `rsx!` always passes children.
37#[proc_macro_attribute]
38pub fn component(attr: TokenStream, item: TokenStream) -> TokenStream {
39    component::expand(attr.into(), item.into()).into()
40}
41
42/// Turn a function into a custom hook.
43///
44/// Adds `#[track_caller]` and wraps the body in
45/// `cx.hook_scope(Location::caller(), |cx| ..)`, so that the hooks inside are
46/// keyed by *where the custom hook was called*. The first `&mut Cx` argument is
47/// the one that gets scoped.
48#[proc_macro_attribute]
49pub fn hook(attr: TokenStream, item: TokenStream) -> TokenStream {
50    hook::expand(attr.into(), item.into()).into()
51}
52
53/// Build a `View` from JSX-like syntax.
54///
55/// ```ignore
56/// rsx! {
57///     <View direction="row" gap={8}>
58///         <Text>"count: "</Text>
59///         if *count > 0 {
60///             <Button on_click={|| *count -= 1}>"-"</Button>
61///         }
62///         for (i, todo) in todos.iter().enumerate() {
63///             <Row key={i} label={todo} />
64///         }
65///     </View>
66/// }
67/// ```
68///
69/// Expands to `::egui_reactor::view(|cx| { .. })`. Elements are component
70/// functions, `{expr}` embeds any `View`, `"text"` is a string literal (bare
71/// text is an error), and `if` / `for` / `match` are the real Rust control-flow
72/// statements.
73#[proc_macro]
74pub fn rsx(input: TokenStream) -> TokenStream {
75    rsx::expand(input.into()).into()
76}