1use proc_macro::TokenStream;
7use proc_macro_crate::{FoundCrate, crate_name};
8use proc_macro2::Span;
9use quote::quote;
10use syn::parse::{Parse, ParseStream};
11use syn::{Error, FnArg, ImplItem, ItemImpl, Result, ReturnType, Token, Type, parse_macro_input};
12
13mod behavior_kw {
14 syn::custom_keyword!(addr);
15 syn::custom_keyword!(message);
16 syn::custom_keyword!(sends);
17 syn::custom_keyword!(births);
18 syn::custom_keyword!(error);
19}
20
21fn behavior_crate() -> Result<proc_macro2::TokenStream> {
22 if std::env::var("CARGO_PKG_NAME").as_deref() == Ok("bombay-behavior") {
23 return Ok(quote!(::behavior));
27 }
28 match crate_name("bombay-behavior") {
29 Ok(FoundCrate::Itself) => Ok(quote!(::behavior)),
30 Ok(FoundCrate::Name(name)) => {
31 let name = syn::Ident::new(&name, Span::call_site());
32 Ok(quote!(::#name))
33 }
34 Err(error) => Err(Error::new(
35 Span::call_site(),
36 format!("could not resolve the bombay-behavior crate: {error}"),
37 )),
38 }
39}
40
41struct BehaviorArgs {
42 addr: Type,
43 message: Type,
44 sends: Type,
45 births: Type,
46 error: Type,
47}
48
49impl Parse for BehaviorArgs {
50 fn parse(input: ParseStream) -> Result<Self> {
51 input.parse::<behavior_kw::addr>()?;
52 input.parse::<Token![=]>()?;
53 let addr = input.parse()?;
54 input.parse::<Token![,]>()?;
55 input.parse::<behavior_kw::message>()?;
56 input.parse::<Token![=]>()?;
57 let message = input.parse()?;
58 input.parse::<Token![,]>()?;
59 input.parse::<behavior_kw::sends>()?;
60 input.parse::<Token![=]>()?;
61 let sends = input.parse()?;
62 input.parse::<Token![,]>()?;
63 input.parse::<behavior_kw::births>()?;
64 input.parse::<Token![=]>()?;
65 let births = input.parse()?;
66 input.parse::<Token![,]>()?;
67 input.parse::<behavior_kw::error>()?;
68 input.parse::<Token![=]>()?;
69 let error = input.parse()?;
70 if input.peek(Token![,]) {
71 input.parse::<Token![,]>()?;
72 }
73 if !input.is_empty() {
74 return Err(input
75 .error("expected exactly addr, message, sends, births, and error in that order"));
76 }
77 Ok(Self {
78 addr,
79 message,
80 sends,
81 births,
82 error,
83 })
84 }
85}
86
87fn validate_receiver(method: &syn::ImplItemFn) -> Result<()> {
88 let Some(FnArg::Receiver(receiver)) = method.sig.inputs.first() else {
89 return Err(Error::new_spanned(
90 &method.sig,
91 "behavior methods must begin with &mut self",
92 ));
93 };
94 if !matches!(receiver.kind, syn::ReceiverKind::Reference(_, _, Some(_))) {
95 return Err(Error::new_spanned(
96 receiver,
97 "behavior methods must begin with &mut self",
98 ));
99 }
100 if method.sig.constness.is_some()
101 || method.sig.asyncness.is_some()
102 || matches!(method.sig.safety, syn::Safety::Unsafe(_))
103 || !method.sig.generics.params.is_empty()
104 {
105 return Err(Error::new_spanned(
106 &method.sig,
107 "behavior init and receive methods must be synchronous, safe, and non-generic",
108 ));
109 }
110 if matches!(method.sig.output, ReturnType::Default) {
111 return Err(Error::new_spanned(
112 &method.sig,
113 "behavior methods must declare their complete Actions result type",
114 ));
115 }
116 Ok(())
117}
118
119#[proc_macro_attribute]
125pub fn behavior(args: TokenStream, item: TokenStream) -> TokenStream {
126 let args = parse_macro_input!(args as BehaviorArgs);
127 let item = parse_macro_input!(item as ItemImpl);
128
129 if item.trait_.is_some() {
130 return Error::new_spanned(
131 &item,
132 "#[behavior] applies to an inherent impl, not a trait impl",
133 )
134 .to_compile_error()
135 .into();
136 }
137
138 let init = item.items.iter().find_map(|item| match item {
139 ImplItem::Fn(method) if method.sig.ident == "init" => Some(method),
140 _ => None,
141 });
142 let receive = item.items.iter().find_map(|item| match item {
143 ImplItem::Fn(method) if method.sig.ident == "receive" => Some(method),
144 _ => None,
145 });
146 let Some(receive) = receive else {
147 return Error::new_spanned(
148 &item.self_ty,
149 "#[behavior] requires a receive(&mut self, from, message) method",
150 )
151 .to_compile_error()
152 .into();
153 };
154 if let Err(error) = init
155 .map_or(Ok(()), validate_receiver)
156 .and_then(|()| validate_receiver(receive))
157 {
158 return error.to_compile_error().into();
159 }
160 if let Some(init) = init
161 && init.sig.inputs.len() != 1
162 {
163 return Error::new_spanned(&init.sig, "init must accept exactly &mut self")
164 .to_compile_error()
165 .into();
166 }
167 if receive.sig.inputs.len() != 3 {
168 return Error::new_spanned(
169 &receive.sig,
170 "receive must accept exactly &mut self, from, and message",
171 )
172 .to_compile_error()
173 .into();
174 }
175
176 let BehaviorArgs {
177 addr,
178 message,
179 sends,
180 births,
181 error,
182 } = args;
183 let self_ty = &item.self_ty;
184 let (impl_generics, _, where_clause) = item.generics.split_for_impl();
185 let behavior = match behavior_crate() {
186 Ok(behavior) => behavior,
187 Err(error) => return error.to_compile_error().into(),
188 };
189 let initialize = init.map_or_else(
190 || quote!(::core::result::Result::Ok(#behavior::Actions::cont())),
191 |_| quote!(<#self_ty>::init(self)),
192 );
193
194 quote! {
195 #item
196
197 impl #impl_generics #behavior::Behavior for #self_ty #where_clause {
198 type Addr = #addr;
199 type Msg = #message;
200 type Event = #behavior::User<#addr, #message>;
201 type Sends = #sends;
202 type Ph = #behavior::Never;
203 type Error = #error;
204 type Birth = #births;
205
206 fn init(
207 &mut self,
208 _: #behavior::InitializationTurn,
209 ) -> #behavior::BehaviorActed<Self> {
210 #initialize
211 }
212
213 fn transition(
214 &mut self,
215 _: #behavior::ActiveTurn,
216 event: Self::Event,
217 ) -> #behavior::BehaviorActed<Self> {
218 <#self_ty>::receive(self, event.from, event.message)
219 }
220 }
221
222 impl #impl_generics #behavior::BehaviorBase for #self_ty #where_clause {
223 type Base = Self;
224
225 fn base(&self) -> &Self {
226 self
227 }
228 }
229 }
230 .into()
231}