Skip to main content

baton_derive/
lib.rs

1//! Derive a struct, creatig a `XxxSend` and `XxxRecv` struct.
2//! Each field has its own channel, allowing you to send and receive updates independently.
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::*;
6
7/*
8// example:
9#[derive(Baton)]
10struct MyStruct {
11    pub field1: i32,
12    pub field2: String,
13}
14
15// expands to:
16struct MyStructSend {
17    pub field1: baton::Send<i32>,
18    pub field2: baton::Send<String>,
19}
20
21#[derive(Clone)]
22struct MyStructRecv {
23    pub field1: baton::Recv<i32>,
24    pub field2: baton::Recv<String>,
25}
26
27impl MyStruct {
28    fn baton(self) -> (MyStructSend, MyStructRecv) {
29        // ...
30    }
31}
32 */
33
34#[proc_macro_derive(Baton)]
35pub fn derive_baton(input: TokenStream) -> TokenStream {
36    let input = parse_macro_input!(input as DeriveInput);
37
38    // Extract the name of the struct
39    let base_name = input.ident;
40    let vis = input.vis;
41
42    // Define names for generated send/recv structs
43    let send_name = syn::Ident::new(&format!("{}Send", base_name), base_name.span());
44    let recv_name = syn::Ident::new(&format!("{}Recv", base_name), base_name.span());
45
46    // Extract fields from the struct
47    let fields = if let Data::Struct(data) = &input.data {
48        match &data.fields {
49            Fields::Named(fields) => &fields.named,
50            _ => panic!("Baton can only be derived for structs with named fields"),
51        }
52    } else {
53        panic!("Baton can only be derived for structs");
54    };
55
56    // Generate fields for send/recv structs
57    let send_fields = fields.iter().map(|f| {
58        let name = &f.ident;
59        let ty = &f.ty;
60        let vis = &f.vis;
61        quote! {
62            #vis #name: ::baton::Send<#ty>,
63        }
64    });
65
66    let recv_fields = fields.iter().map(|f| {
67        let name = &f.ident;
68        let ty = &f.ty;
69        let vis = &f.vis;
70        quote! {
71            #vis #name: ::baton::Recv<#ty>,
72        }
73    });
74
75    // Generate the baton function
76    let baton_fn_fields = fields.iter().map(|f| {
77        let name = &f.ident;
78
79        quote! {
80            let #name = ::baton::channel(self.#name);
81        }
82    });
83
84    let baton_fn_return_send = fields.iter().map(|f| {
85        let name = &f.ident;
86        quote! {
87            #name: #name.0,
88        }
89    });
90
91    let baton_fn_return_recv = fields.iter().map(|f| {
92        let name = &f.ident;
93        quote! {
94            #name: #name.1,
95        }
96    });
97
98    // Generate the output tokens
99    let expanded = quote! {
100        #[derive(Clone)]
101        #vis struct #send_name {
102            #(#send_fields)*
103        }
104
105        #[derive(Clone)]
106        #vis struct #recv_name {
107            #(#recv_fields)*
108        }
109
110        impl #base_name {
111            pub fn baton(self) -> (#send_name, #recv_name) {
112                #(#baton_fn_fields)*
113
114                (
115                    #send_name {
116                        #(#baton_fn_return_send)*
117                    },
118                    #recv_name {
119                        #(#baton_fn_return_recv)*
120                    }
121                )
122            }
123        }
124    };
125
126    TokenStream::from(expanded)
127}