acton_macro/
lib.rs

1/*
2 * Copyright (c) 2024. Govcraft
3 *
4 * Licensed under either of
5 *   * Apache License, Version 2.0 (the "License");
6 *     you may not use this file except in compliance with the License.
7 *     You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8 *   * MIT license: http://opensource.org/licenses/MIT
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the applicable License for the specific language governing permissions and
14 * limitations under that License.
15 */
16#![forbid(unsafe_code)]
17
18//! Acton Macro Library
19//!
20//! This library provides procedural macros for the Acton actor framework.
21//! It includes macros to derive common traits and boilerplate code for Acton messages.
22
23extern crate proc_macro;
24use proc_macro::TokenStream;
25
26use quote::quote;
27use syn::{parse_macro_input, DeriveInput};
28
29fn has_derive(input: &DeriveInput, trait_name: &str) -> bool {
30    input.attrs.iter().any(|attr| {
31        if attr.path().is_ident("derive") {
32            let mut found = false;
33            let _ = attr.parse_nested_meta(|meta| {
34                if meta.path.is_ident(trait_name) {
35                    found = true;
36                }
37                Ok(())
38            });
39            found
40        } else {
41            false
42        }
43    })
44}
45
46
47/// A procedural macro to derive the necessary traits for an Acton message.
48///
49/// This macro will automatically implement `Clone`, `Debug`, and `ActonMessage`
50/// for the annotated type. The `Sync` trait is implemented automatically by the
51/// compiler when possible, so an explicit unsafe implementation is unnecessary.
52#[proc_macro_attribute]
53pub fn acton_message(_attr: TokenStream, item: TokenStream) -> TokenStream {
54    // Parse the input tokens into a syntax tree.
55    let input = parse_macro_input!(item as DeriveInput);
56
57    // Get the name and generics of the struct.
58    let name = &input.ident;
59    let generics = &input.generics;
60    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
61
62    // Generate the expanded code.
63    let clone_attr = if has_derive(&input, "Clone") {
64        quote!()
65    } else {
66        quote!(#[derive(Clone)])
67    };
68
69    let expanded = quote! {
70        // Derive the Clone trait if not already present.
71        #clone_attr
72        #input
73
74        // Implement the Debug trait.
75        impl #impl_generics std::fmt::Debug for #name #ty_generics #where_clause {
76            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77                write!(f, stringify!(#name))
78            }
79        }
80
81    };
82
83    // Return the generated tokens.
84    TokenStream::from(expanded)
85}
86
87/// A procedural macro to derive boilerplate traits for Acton actors.
88///
89/// This macro ensures the annotated type implements `Default`, `Clone`, and
90/// `Debug`. Existing derives for these traits are preserved.
91#[proc_macro_attribute]
92pub fn acton_actor(_attr: TokenStream, item: TokenStream) -> TokenStream {
93    // Parse the input tokens into a syntax tree.
94    let input = parse_macro_input!(item as DeriveInput);
95
96    // Get the name and generics of the struct.
97    let name = &input.ident;
98    let generics = &input.generics;
99    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
100
101    // Generate the expanded code.
102    let need_default = !has_derive(&input, "Default");
103    let need_clone = !has_derive(&input, "Clone");
104    let derives = {
105        let mut traits = Vec::new();
106        if need_default { traits.push(quote!(Default)); }
107        if need_clone { traits.push(quote!(Clone)); }
108        if traits.is_empty() { quote!() } else { quote!(#[derive(#(#traits),*)]) }
109    };
110
111    let expanded = quote! {
112        // Derive Default and Clone if not already present.
113        #derives
114        #input
115
116        // Implement the Debug trait.
117        impl #impl_generics std::fmt::Debug for #name #ty_generics #where_clause {
118            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119                write!(f, stringify!(#name))
120            }
121        }
122    };
123
124    // Return the generated tokens.
125    TokenStream::from(expanded)
126}