Skip to main content

kstool_helper_generator/
lib.rs

1mod basic;
2mod bit;
3mod enchanted;
4
5/// Derive macro that generates an MPSC channel helper struct for an enum.
6///
7/// The enum name must contain "Event" (e.g., `MyEvent`). The macro generates:
8/// - A helper struct named `{Prefix}Helper` (e.g., `MyHelper` from `MyEvent`)
9/// - A receiver type alias named `{EnumName}Receiver`
10/// - Async send methods for each enum variant (names converted to snake_case)
11/// - A `new(size)` constructor returning `(Helper, Receiver)` tuple
12/// - A `From<mpsc::Sender>` impl for the helper struct
13///
14/// # Attributes
15///
16/// - `#[helper(block)]` on the enum or variant: also generate blocking send methods (suffixed with `_b`)
17/// - `#[helper(no_async)]` on the enum or variant: generate only blocking send methods (no async)
18///
19/// # Example
20///
21/// ```rust,ignore
22/// #[derive(Helper)]
23/// enum MyEvent {
24///     UserLogin { username: String },
25///     UserLogout,
26///     DataUpdate(Vec<u8>),
27/// }
28/// // Generates: MyHelper with async methods user_login(), user_logout(), data_update()
29/// ```
30#[proc_macro_derive(Helper, attributes(helper))]
31pub fn enum_helper_generator(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
32    let st = syn::parse_macro_input!(input as syn::DeriveInput);
33    //eprintln!("{:#?}", st.attrs);
34    //eprintln!("{st:#?}");
35
36    if let Err(e) = crate::basic::early_check(&st) {
37        return e.into_compile_error().into();
38    }
39
40    basic::do_expand(&st, None)
41        .unwrap_or_else(syn::Error::into_compile_error)
42        .into()
43}
44
45/// Procedural macro that generates a bit flag enum with checker methods.
46///
47/// Each variant is assigned a unique power-of-two value (`1 << index`), and a
48/// corresponding `is_{snake_case_name}()` method is generated on the enum.
49///
50/// The macro also derives `Clone`, `Copy`, and `Debug` for the generated enum.
51///
52/// # Example
53///
54/// ```rust,ignore
55/// bit_helper! {
56///     enum Features {
57///         Analytics,      // = 1
58///         Notifications,  // = 2
59///         DarkMode,       // = 4
60///     }
61/// }
62/// // Generates: Features with methods is_analytics(), is_notifications(), is_dark_mode()
63/// ```
64#[proc_macro]
65pub fn bit_helper(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
66    let st = syn::parse_macro_input!(input as syn::DeriveInput);
67    //eprintln!("{:#?}", st.attrs);
68
69    bit::parse_bit(st)
70        .unwrap_or_else(|x| x.into_compile_error())
71        .into()
72}
73
74/// Procedural macro that generates an MPSC helper with request-response support
75/// via oneshot channels.
76///
77/// Similar to `#[derive(Helper)]`, but variants annotated with `#[ret(Type)]` will
78/// have their generated methods return `Option<Type>` instead of `Option<()>`. The
79/// macro automatically injects a `tokio::sync::oneshot::Sender` field into each
80/// annotated variant and manages the oneshot channel lifecycle.
81///
82/// Variants without `#[ret(...)]` behave identically to the basic `Helper` derive.
83///
84/// The enum is re-emitted with the additional sender fields, so it must be used
85/// as `oneshot_helper! { ... }` rather than as a derive macro.
86///
87/// # Example
88///
89/// ```rust,ignore
90/// oneshot_helper! {
91///     enum QueryEvent {
92///         #[ret(String)]
93///         GetConfig { key: String },
94///         #[ret(bool)]
95///         IsEnabled { feature: &'static str },
96///         Shutdown,
97///     }
98/// }
99/// // Generates: QueryHelper with methods:
100/// //   async fn get_config(&self, key: String) -> Option<String>
101/// //   async fn is_enabled(&self, feature: &'static str) -> Option<bool>
102/// //   async fn shutdown(&self) -> Option<()>
103/// ```
104#[proc_macro]
105pub fn oneshot_helper(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
106    let early_st = syn::parse_macro_input!(input as syn::DeriveInput);
107    enchanted::handle_new(early_st)
108        .unwrap_or_else(syn::Error::into_compile_error)
109        .into()
110}