1use proc_macro::TokenStream;
7use proc_macro_crate::{FoundCrate, crate_name};
8use proc_macro2::{Span, TokenStream as TokenStream2};
9use quote::{format_ident, quote};
10use syn::parse::{Parse, ParseStream};
11use syn::visit::Visit;
12use syn::{
13 Data, DeriveInput, Error, Fields, FnArg, GenericArgument, GenericParam, Generics, Ident,
14 ImplItem, ItemImpl, PathArguments, Result, ReturnType, Token, Type, Visibility, braced,
15 parse_macro_input, parse_quote,
16};
17
18fn crate_path(found: FoundCrate) -> TokenStream2 {
19 match found {
20 FoundCrate::Itself => quote!(crate),
21 FoundCrate::Name(name) => {
22 let name = match name.as_str() {
25 "bombay_behavior" => "behavior",
26 "bombay_behavior_actors" => "behavior_actors",
27 "bombay_rs" => "bombay",
28 _ => &name,
29 };
30 let name = syn::Ident::new(&name, Span::call_site());
31 quote!(::#name)
32 }
33 }
34}
35
36fn behavior_crate() -> Result<TokenStream2> {
37 if std::env::var("CARGO_PKG_NAME").as_deref() == Ok("bombay-behavior") {
38 return Ok(quote!(::behavior));
42 }
43 if std::env::var("CARGO_PKG_NAME").as_deref() == Ok("bombay-rs") {
44 return Ok(quote!(::bombay::behavior));
50 }
51 if let Ok(found) = crate_name("bombay-behavior") {
52 return Ok(crate_path(found));
53 }
54 if let Ok(found) = crate_name("bombay-rs") {
55 let bombay = crate_path(found);
56 return Ok(quote!(#bombay::behavior));
57 }
58 Err(Error::new(
59 Span::call_site(),
60 "could not resolve `bombay-behavior` directly or through `bombay-rs`",
61 ))
62}
63
64fn actors_crate() -> Result<TokenStream2> {
65 if std::env::var("CARGO_PKG_NAME").as_deref() == Ok("bombay-behavior-actors") {
66 return if std::env::var("CARGO_CRATE_NAME").as_deref() == Ok("behavior_actors") {
67 Ok(quote!(crate))
68 } else {
69 Ok(quote!(::behavior_actors))
70 };
71 }
72 if std::env::var("CARGO_PKG_NAME").as_deref() == Ok("bombay-rs") {
73 return Ok(quote!(::bombay));
74 }
75 if let Ok(found) = crate_name("bombay-rs") {
83 return Ok(crate_path(found));
84 }
85 if let Ok(found) = crate_name("bombay-behavior-actors") {
86 return Ok(crate_path(found));
87 }
88 Err(Error::new(
89 Span::call_site(),
90 "could not resolve `bombay-behavior-actors` through `bombay-rs` or directly",
91 ))
92}
93
94#[cfg(test)]
95mod crate_resolution_tests {
96 use super::*;
97
98 #[test]
99 fn package_library_names_and_explicit_renames_resolve_distinctly() {
100 assert_eq!(
101 crate_path(FoundCrate::Name("bombay_rs".to_owned())).to_string(),
102 ":: bombay"
103 );
104 assert_eq!(
105 crate_path(FoundCrate::Name("bombay_behavior".to_owned())).to_string(),
106 ":: behavior"
107 );
108 assert_eq!(
109 crate_path(FoundCrate::Name("bombay_behavior_actors".to_owned())).to_string(),
110 ":: behavior_actors"
111 );
112 assert_eq!(
113 crate_path(FoundCrate::Name("runtime".to_owned())).to_string(),
114 ":: runtime"
115 );
116 }
117}
118
119struct NamedField {
120 name: Ident,
121 ty: Type,
122}
123
124struct NamedProduct {
125 fields: Vec<NamedField>,
126}
127
128#[allow(
129 clippy::large_enum_variant,
130 reason = "boxing syn::Type would add lint-driven indirection to compile-time parser state"
131)]
132enum SendsSpec {
133 Existing(Type),
134 Generated {
135 product: NamedProduct,
136 visibility: Visibility,
137 },
138}
139
140#[allow(
141 clippy::large_enum_variant,
142 reason = "boxing syn::Type would add lint-driven indirection to compile-time parser state"
143)]
144enum BirthsSpec {
145 Existing(Type),
146 Generated(NamedProduct),
147}
148
149#[derive(Clone, Copy)]
150enum CreationSettlementDisposition {
151 ReturnToCreator,
152 RetainForRetirement,
153}
154
155fn parse_product(input: ParseStream) -> Result<NamedProduct> {
156 let content;
157 braced!(content in input);
158 let mut fields = Vec::new();
159 let mut names = std::collections::BTreeSet::new();
160 while !content.is_empty() {
161 let name: Ident = content.parse()?;
162 if !names.insert(name.to_string()) {
163 return Err(Error::new_spanned(name, "duplicate product lane"));
164 }
165 content.parse::<Token![:]>()?;
166 fields.push(NamedField {
167 name,
168 ty: content.parse()?,
169 });
170 if content.peek(Token![,]) {
171 content.parse::<Token![,]>()?;
172 } else if !content.is_empty() {
173 return Err(content.error("expected `,` between product lanes"));
174 }
175 }
176 if fields.is_empty() {
177 return Err(input.error("empty products must be omitted"));
178 }
179 Ok(NamedProduct { fields })
180}
181
182struct BehaviorArgs {
183 addr: Type,
184 message: Type,
185 sends: Option<SendsSpec>,
186 births: Option<BirthsSpec>,
187 creation_settlements: Option<CreationSettlementDisposition>,
188 error: Option<Type>,
189}
190
191struct PoolWorkerArgs {
192 addr: Type,
193 result: Type,
194}
195
196impl Parse for PoolWorkerArgs {
197 fn parse(input: ParseStream) -> Result<Self> {
198 let mut addr = None;
199 let mut result = None;
200 while !input.is_empty() {
201 let key: Ident = input.parse()?;
202 input.parse::<Token![=]>()?;
203 match key.to_string().as_str() {
204 "addr" if addr.is_none() => addr = Some(input.parse()?),
205 "result" if result.is_none() => result = Some(input.parse()?),
206 "addr" | "result" => {
207 return Err(Error::new_spanned(key, "duplicate pool worker argument"));
208 }
209 _ => return Err(Error::new_spanned(key, "unknown pool worker argument")),
210 }
211 if input.peek(Token![,]) {
212 input.parse::<Token![,]>()?;
213 } else if !input.is_empty() {
214 return Err(input.error("expected `,` between pool worker arguments"));
215 }
216 }
217 Ok(Self {
218 addr: addr.ok_or_else(|| input.error("missing `addr`"))?,
219 result: result.ok_or_else(|| input.error("missing `result`"))?,
220 })
221 }
222}
223
224impl Parse for BehaviorArgs {
225 fn parse(input: ParseStream) -> Result<Self> {
226 let mut addr = None;
227 let mut message = None;
228 let mut sends = None;
229 let mut births = None;
230 let mut creation_settlements = None;
231 let mut error = None;
232 while !input.is_empty() {
233 let key: Ident = input.parse()?;
234 input.parse::<Token![=]>()?;
235 match key.to_string().as_str() {
236 "addr" if addr.is_none() => addr = Some(input.parse()?),
237 "message" if message.is_none() => message = Some(input.parse()?),
238 "sends" if sends.is_none() => {
239 let visibility: Visibility = input.parse()?;
240 sends = Some(if input.peek(syn::token::Brace) {
241 SendsSpec::Generated {
242 product: parse_product(input)?,
243 visibility,
244 }
245 } else if matches!(visibility, Visibility::Inherited) {
246 SendsSpec::Existing(input.parse()?)
247 } else {
248 return Err(input.error("send-product visibility requires named lanes"));
249 });
250 }
251 "births" if births.is_none() => {
252 births = Some(if input.peek(syn::token::Brace) {
253 BirthsSpec::Generated(parse_product(input)?)
254 } else {
255 BirthsSpec::Existing(input.parse()?)
256 });
257 }
258 "creation_settlements" if creation_settlements.is_none() => {
259 let disposition: Ident = input.parse()?;
260 creation_settlements = Some(match disposition.to_string().as_str() {
261 "return_to_creator" => CreationSettlementDisposition::ReturnToCreator,
262 "retain_for_retirement" => {
263 CreationSettlementDisposition::RetainForRetirement
264 }
265 _ => {
266 return Err(Error::new_spanned(
267 disposition,
268 "creation settlements must `return_to_creator` or `retain_for_retirement`",
269 ));
270 }
271 });
272 }
273 "error" if error.is_none() => error = Some(input.parse()?),
274 "addr" | "message" | "sends" | "births" | "creation_settlements" | "error" => {
275 return Err(Error::new_spanned(key, "duplicate behavior argument"));
276 }
277 _ => return Err(Error::new_spanned(key, "unknown behavior argument")),
278 }
279 if input.peek(Token![,]) {
280 input.parse::<Token![,]>()?;
281 } else if !input.is_empty() {
282 return Err(input.error("expected `,` between behavior arguments"));
283 }
284 }
285 match (&births, creation_settlements) {
286 (Some(BirthsSpec::Generated(_)), None) => {
287 return Err(input.error(
288 "birth-owning behaviors must declare `creation_settlements = return_to_creator` or `creation_settlements = retain_for_retirement`",
289 ));
290 }
291 (None, Some(_)) => {
292 return Err(input.error("`creation_settlements` requires a `births` declaration"));
293 }
294 (Some(_), Some(_)) | (Some(_), None) | (None, None) => {}
295 }
296 Ok(Self {
297 addr: addr.ok_or_else(|| input.error("missing `addr`"))?,
298 message: message.ok_or_else(|| input.error("missing `message`"))?,
299 sends,
300 births,
301 creation_settlements,
302 error,
303 })
304 }
305}
306
307fn validate_receiver(method: &syn::ImplItemFn) -> Result<()> {
308 let Some(FnArg::Receiver(receiver)) = method.sig.inputs.first() else {
309 return Err(Error::new_spanned(
310 &method.sig,
311 "behavior methods must begin with &mut self",
312 ));
313 };
314 if !matches!(receiver.kind, syn::ReceiverKind::Reference(_, _, Some(_))) {
315 return Err(Error::new_spanned(
316 receiver,
317 "behavior methods must begin with &mut self",
318 ));
319 }
320 if method.sig.constness.is_some()
321 || method.sig.asyncness.is_some()
322 || matches!(method.sig.safety, syn::Safety::Unsafe(_))
323 || !method.sig.generics.params.is_empty()
324 {
325 return Err(Error::new_spanned(
326 &method.sig,
327 "behavior methods must be synchronous, safe, and non-generic",
328 ));
329 }
330 if matches!(method.sig.output, ReturnType::Default) {
331 return Err(Error::new_spanned(
332 &method.sig,
333 "behavior methods must declare their complete Actions result type",
334 ));
335 }
336 Ok(())
337}
338
339fn validate_pool_worker_output(output: &ReturnType) -> Result<()> {
340 let ReturnType::Type(_, output) = output else {
341 return Err(Error::new_spanned(
342 output,
343 "pool worker transition must return WorkerActed<Self>",
344 ));
345 };
346 let Type::Path(output) = output.as_ref() else {
347 return Err(Error::new_spanned(
348 output,
349 "pool worker transition must return WorkerActed<Self>",
350 ));
351 };
352 if output.qself.is_some() || output.path.segments.len() != 1 {
353 return Err(Error::new_spanned(
354 output,
355 "pool worker transition must return WorkerActed<Self>",
356 ));
357 }
358 let segment = output
359 .path
360 .segments
361 .first()
362 .ok_or_else(|| Error::new_spanned(output, "missing pool worker return type"))?;
363 let PathArguments::AngleBracketed(arguments) = &segment.arguments else {
364 return Err(Error::new_spanned(
365 output,
366 "pool worker transition must return WorkerActed<Self>",
367 ));
368 };
369 let mut arguments = arguments.args.iter();
370 let Some(GenericArgument::Type(Type::Path(self_type))) = arguments.next() else {
371 return Err(Error::new_spanned(
372 output,
373 "pool worker transition must return WorkerActed<Self>",
374 ));
375 };
376 let self_segment = self_type.path.segments.first();
377 if segment.ident != "WorkerActed"
378 || arguments.next().is_some()
379 || self_type.qself.is_some()
380 || self_type.path.segments.len() != 1
381 || !matches!(self_segment, Some(segment) if segment.ident == "Self" && matches!(segment.arguments, PathArguments::None))
382 {
383 return Err(Error::new_spanned(
384 output,
385 "pool worker transition must return WorkerActed<Self>",
386 ));
387 }
388 Ok(())
389}
390
391fn pool_worker_assignment(method: &syn::ImplItemFn) -> Result<Type> {
392 let Some(FnArg::Receiver(receiver)) = method.sig.inputs.first() else {
393 return Err(Error::new_spanned(
394 &method.sig,
395 "pool worker transition must begin with &mut self",
396 ));
397 };
398 if !matches!(receiver.kind, syn::ReceiverKind::Reference(_, _, Some(_))) {
399 return Err(Error::new_spanned(
400 receiver,
401 "pool worker transition must begin with &mut self",
402 ));
403 }
404 if method.sig.constness.is_some()
405 || method.sig.asyncness.is_some()
406 || matches!(method.sig.safety, syn::Safety::Unsafe(_))
407 || method.sig.abi.is_some()
408 || method.sig.variadic.is_some()
409 || !method.sig.generics.params.is_empty()
410 {
411 return Err(Error::new_spanned(
412 &method.sig,
413 "pool worker transition must be synchronous, safe, and non-generic",
414 ));
415 }
416 if method.sig.inputs.len() != 2 {
417 return Err(Error::new_spanned(
418 &method.sig,
419 "pool worker transition must accept exactly &mut self and one Assignment<Job>",
420 ));
421 }
422 validate_pool_worker_output(&method.sig.output)?;
423
424 let Some(FnArg::Typed(assignment)) = method.sig.inputs.iter().nth(1) else {
425 return Err(Error::new_spanned(
426 &method.sig,
427 "pool worker transition must accept one Assignment<Job>",
428 ));
429 };
430 let Type::Path(assignment_path) = assignment.ty.as_ref() else {
431 return Err(Error::new_spanned(
432 &assignment.ty,
433 "pool worker transition input must be Assignment<Job>",
434 ));
435 };
436 let Some(segment) = assignment_path.path.segments.last() else {
437 return Err(Error::new_spanned(
438 &assignment.ty,
439 "pool worker transition input must be Assignment<Job>",
440 ));
441 };
442 let PathArguments::AngleBracketed(arguments) = &segment.arguments else {
443 return Err(Error::new_spanned(
444 &assignment.ty,
445 "pool worker transition input must be Assignment<Job>",
446 ));
447 };
448 let mut arguments = arguments.args.iter();
449 if segment.ident != "Assignment"
450 || !matches!(arguments.next(), Some(GenericArgument::Type(_)))
451 || arguments.next().is_some()
452 {
453 return Err(Error::new_spanned(
454 &assignment.ty,
455 "pool worker transition input must be Assignment<Job>",
456 ));
457 }
458 Ok(assignment.ty.as_ref().clone())
459}
460
461fn pascal_name(field: &Ident) -> String {
462 field
463 .to_string()
464 .split('_')
465 .filter(|part| !part.is_empty())
466 .map(|part| {
467 let mut chars = part.chars();
468 chars.next().map_or_else(String::new, |first| {
469 first.to_uppercase().chain(chars).collect()
470 })
471 })
472 .collect::<String>()
473}
474
475fn lane_name(product: &Ident, field: &Ident) -> Ident {
476 let lane = pascal_name(field);
477 format_ident!("{}{}", product, lane)
478}
479
480#[derive(Default)]
481struct GenericUses {
482 identifiers: std::collections::BTreeSet<String>,
483 lifetimes: std::collections::BTreeSet<String>,
484}
485
486impl GenericUses {
487 fn fresh(&mut self, preferred: &str) -> Ident {
490 let mut name = preferred.to_owned();
491 while self.identifiers.contains(&name) || self.identifiers.contains(&format!("r#{name}")) {
492 name.push('_');
493 }
494 self.identifiers.insert(name.clone());
495 Ident::new(&name, Span::mixed_site())
496 }
497}
498
499impl<'ast> Visit<'ast> for GenericUses {
500 fn visit_ident(&mut self, ident: &'ast Ident) {
501 self.identifiers.insert(ident.to_string());
502 }
503
504 fn visit_lifetime(&mut self, lifetime: &'ast syn::Lifetime) {
505 self.lifetimes.insert(lifetime.ident.to_string());
506 }
507}
508
509fn product_generics(item: &ItemImpl, fields: &[NamedField]) -> Generics {
510 let mut uses = GenericUses::default();
511 for field in fields {
512 uses.visit_type(&field.ty);
513 }
514 let mut generics = item.generics.clone();
515 generics.params = generics
516 .params
517 .into_iter()
518 .filter(|parameter| match parameter {
519 GenericParam::Lifetime(parameter) => uses
520 .lifetimes
521 .contains(¶meter.lifetime.ident.to_string()),
522 GenericParam::Type(parameter) => {
523 uses.identifiers.contains(¶meter.ident.to_string())
524 }
525 GenericParam::Const(parameter) => {
526 uses.identifiers.contains(¶meter.ident.to_string())
527 }
528 })
529 .collect();
530 generics.where_clause = None;
531 generics
532}
533
534fn named_send_interpretation(
535 loans: &[&Ident],
536 field_types: &[&Type],
537 interpreter: &Ident,
538 root_event: &Ident,
539 path: &Ident,
540 behavior: &TokenStream2,
541) -> TokenStream2 {
542 let turns = loans.iter().zip(field_types).enumerate().map(|(index, (loan, field_ty))| {
543 let current_loan = loans.iter().enumerate().map(|(position, retained)| {
544 if position == index { quote!(#retained) } else { quote!(_) }
545 }).collect::<Vec<_>>();
546 let remaining_loans = loans.iter().enumerate().map(|(position, retained)| {
547 if position >= index { quote!(#retained) } else { quote!(_) }
548 }).collect::<Vec<_>>();
549 let later_loans = &loans[index + 1..];
550 let later_types = &field_types[index + 1..];
551 quote! {
552 {
555 let ::core::option::Option::Some(#behavior::InterpretationProgress::Interpreting(custody)) = progress else { return; };
556 let (#(#current_loan,)*) = custody;
557 <#field_ty as #behavior::InterpretSends<#interpreter, #root_event, #path>>::interpret(#loan, interpreter)
558 }.await;
559 {
562 let ::core::option::Option::Some(#behavior::InterpretationProgress::Interpreting(custody)) = progress else { return; };
563 let (#(#remaining_loans,)*) = custody;
564 <#field_ty as #behavior::SendSettlements>::finish_interpretation(#loan);
565 match #loan {
566 ::core::option::Option::Some(#behavior::InterpretationProgress::Completed(#behavior::Interpretation::Complete(_))) => {},
567 ::core::option::Option::Some(#behavior::InterpretationProgress::Completed(#behavior::Interpretation::Corrupt(_))) => {
568 #(<#later_types as #behavior::SendSettlements>::unattempted(#later_loans);)*
569 <Self as #behavior::SendSettlements>::finish_interpretation(progress);
570 return;
571 }
572 _ => return,
573 }
574 }
575 }
576 }).collect::<Vec<_>>();
577 quote! {
578 <Self as #behavior::SendSettlements>::prepare_interpretation(progress);
579 #(#turns)*
580 <Self as #behavior::SendSettlements>::finish_interpretation(progress);
581 }
582}
583
584fn source_product_custody(
585 settlements: &[TokenStream2],
586 custody: &[TokenStream2],
587 behavior: &TokenStream2,
588) -> (TokenStream2, TokenStream2) {
589 settlements.iter().zip(custody).rev().fold((quote!(#behavior::NoSends), quote!(#behavior::NoSends)), |(later_settlements, later_custody), (earlier_settlements, earlier_custody)| (
590 quote!((#earlier_settlements, #later_settlements)),
591 quote!((::core::option::Option<#behavior::SourceProgress<#earlier_settlements, #earlier_custody>>, ::core::option::Option<#behavior::SourceProgress<#later_settlements, #later_custody>>)),
592 ))
593}
594
595fn named_settlement_contract(
596 name: &Ident,
597 settlement_type: &TokenStream2,
598 generics: &Generics,
599 fields: &[&Ident],
600 field_types: &[Type],
601 behavior: &TokenStream2,
602) -> TokenStream2 {
603 let mut uses = GenericUses::default();
604 uses.visit_generics(generics);
605 uses.visit_ident(name);
606 for field_ty in field_types {
607 uses.visit_type(field_ty);
608 }
609 let settlement_host = uses.fresh("__BombaySettlementHost");
610 let settlement_event = uses.fresh("__BombaySettlementEvent");
611 let mut classification_generics = generics.clone();
612 let mut custody_generics = generics.clone();
613 custody_generics.params.push(parse_quote!(#settlement_host));
614 custody_generics
615 .params
616 .push(parse_quote!(#settlement_event));
617 custody_generics
618 .make_where_clause()
619 .predicates
620 .push(parse_quote!(#settlement_host: ::core::marker::Send));
621 let mut combined_status = quote!(#behavior::SettlementStatus::Accepted);
622 for (field, field_ty) in fields.iter().zip(field_types) {
623 classification_generics
624 .make_where_clause()
625 .predicates
626 .push(parse_quote!(#field_ty: #behavior::ClassifySettlement));
627 custody_generics
628 .make_where_clause()
629 .predicates
630 .push(parse_quote!(
631 #field_ty: #behavior::SourceSettlementCustody<
632 #settlement_host,
633 #settlement_event,
634 > + ::core::marker::Send
635 ));
636 combined_status = quote! {
637 (#combined_status).combine(
638 #behavior::ClassifySettlement::settlement_status(&self.#field)
639 )
640 };
641 }
642 let (classification_impl, _, classification_where) = classification_generics.split_for_impl();
643 let (custody_impl, _, custody_where) = custody_generics.split_for_impl();
644 let source_settlements = field_types.iter().map(|ty| quote!(#ty)).collect::<Vec<_>>();
645 let source_custody = field_types.iter().map(|ty| quote!(<#ty as #behavior::SourceSettlementCustody<#settlement_host, #settlement_event>>::Custody)).collect::<Vec<_>>();
646 let (source_product, source_owner) =
647 source_product_custody(&source_settlements, &source_custody, behavior);
648 let source_input = fields.iter().rev().fold(
649 quote!(#behavior::NoSends),
650 |later, field| quote!((original.#field, #later)),
651 );
652 let source_output = fields
653 .iter()
654 .rev()
655 .fold(quote!(_), |later, field| quote!((#field, #later)));
656
657 quote! {
658 impl #classification_impl #behavior::ClassifySettlement
659 for #settlement_type #classification_where
660 {
661 fn settlement_status(&self) -> #behavior::SettlementStatus {
662 #combined_status
663 }
664 }
665
666 impl #custody_impl #behavior::SourceSettlementCustody<#settlement_host, #settlement_event> for #settlement_type #custody_where {
667 type Custody = #source_owner;
668 fn prepare_source(progress: &mut ::core::option::Option<#behavior::SourceProgress<Self, Self::Custody>>) {
669 let original = match progress.take() {
670 ::core::option::Option::Some(#behavior::SourceProgress::Original(original)) => original,
671 retained => { *progress = retained; return; }
672 };
673 let mut product = ::core::option::Option::Some(#behavior::SourceProgress::Original(#source_input));
675 <#source_product as #behavior::SourceSettlementCustody<#settlement_host, #settlement_event>>::prepare_source(&mut product);
676 *progress = match product {
677 ::core::option::Option::Some(#behavior::SourceProgress::Original(#source_output)) => ::core::option::Option::Some(#behavior::SourceProgress::Original(#name { #(#fields,)* })),
678 ::core::option::Option::Some(#behavior::SourceProgress::Offering(custody)) => ::core::option::Option::Some(#behavior::SourceProgress::Offering(custody)),
679 ::core::option::Option::Some(#behavior::SourceProgress::Completed(reply)) => ::core::option::Option::Some(#behavior::SourceProgress::Completed(reply.map(|#source_output| #name { #(#fields,)* }))),
680 ::core::option::Option::None => ::core::option::Option::None,
681 };
682 }
683 fn offer_next_to_source(custody: &mut Self::Custody, host: &mut #settlement_host) -> impl ::core::future::Future<Output = ()> + ::core::marker::Send {
684 <#source_product as #behavior::SourceSettlementCustody<#settlement_host, #settlement_event>>::offer_next_to_source(custody, host)
685 }
686 fn finish_source(progress: &mut ::core::option::Option<#behavior::SourceProgress<Self, Self::Custody>>) {
687 let custody = match progress.take() {
688 ::core::option::Option::Some(#behavior::SourceProgress::Offering(custody)) => custody,
689 retained => { *progress = retained; return; }
690 };
691 let mut product = ::core::option::Option::Some(#behavior::SourceProgress::Offering(custody));
692 <#source_product as #behavior::SourceSettlementCustody<#settlement_host, #settlement_event>>::finish_source(&mut product);
693 *progress = match product {
694 ::core::option::Option::Some(#behavior::SourceProgress::Original(#source_output)) => ::core::option::Option::Some(#behavior::SourceProgress::Original(#name { #(#fields,)* })),
695 ::core::option::Option::Some(#behavior::SourceProgress::Offering(custody)) => ::core::option::Option::Some(#behavior::SourceProgress::Offering(custody)),
696 ::core::option::Option::Some(#behavior::SourceProgress::Completed(reply)) => ::core::option::Option::Some(#behavior::SourceProgress::Completed(reply.map(|#source_output| #name { #(#fields,)* }))),
697 ::core::option::Option::None => ::core::option::Option::None,
698 };
699 }
700 }
701
702 }
703}
704
705#[allow(
706 clippy::too_many_lines,
707 reason = "the ordered send-product traits share one named-lane equation"
708)]
709fn named_send_contract(
710 name: &Ident,
711 generics: &Generics,
712 fields: &[&Ident],
713 field_types: &[&Type],
714 settlement_type: &TokenStream2,
715 settlement_name: &Ident,
716 behavior: &TokenStream2,
717) -> TokenStream2 {
718 let mut uses = GenericUses::default();
719 uses.visit_generics(generics);
720 uses.visit_ident(name);
721 for field_ty in field_types {
722 uses.visit_type(field_ty);
723 }
724 let event = uses.fresh("__BombayEvent");
725 let interpreter = uses.fresh("__BombayInterpreter");
726 let root_event = uses.fresh("__BombayRootEvent");
727 let path = uses.fresh("__BombayPath");
728 let (_, type_generics, _) = generics.split_for_impl();
729 let mut effects_generics = generics.clone();
730 let mut logical_generics = generics.clone();
731 let mut settlement_generics = generics.clone();
732 let mut lawful_generics = generics.clone();
733 lawful_generics.params.push(parse_quote!(#event));
734 let mut interpretation_generics = generics.clone();
735 interpretation_generics
736 .params
737 .push(parse_quote!(#interpreter));
738 interpretation_generics
739 .params
740 .push(parse_quote!(#root_event));
741 interpretation_generics.params.push(parse_quote!(#path));
742 interpretation_generics
743 .make_where_clause()
744 .predicates
745 .push(parse_quote!(#interpreter: ::core::marker::Send));
746 interpretation_generics
747 .make_where_clause()
748 .predicates
749 .push(parse_quote!(#name #type_generics: ::core::marker::Send));
750 let mut logical_protocols = quote!(#behavior::NoBirthProtocols);
751 for field_ty in field_types {
752 effects_generics
753 .make_where_clause()
754 .predicates
755 .push(parse_quote!(#field_ty: #behavior::SendEffects));
756 logical_generics
757 .make_where_clause()
758 .predicates
759 .push(parse_quote!(#field_ty: #behavior::LogicalDeliveryProtocols));
760 settlement_generics
761 .make_where_clause()
762 .predicates
763 .push(parse_quote!(#field_ty: #behavior::SendSettlements));
764 lawful_generics
765 .make_where_clause()
766 .predicates
767 .push(parse_quote!(#field_ty: #behavior::SendsFor<#event>));
768 interpretation_generics
769 .make_where_clause()
770 .predicates
771 .push(parse_quote!(
772 #field_ty: #behavior::InterpretSends<
773 #interpreter,
774 #root_event,
775 #path,
776 >
777 ));
778 logical_protocols = quote!(
779 <#logical_protocols as #behavior::BirthProtocolProduct>::Append<
780 <#field_ty as #behavior::LogicalDeliveryProtocols>::Protocols
781 >
782 );
783 }
784 interpretation_generics.make_where_clause().predicates.push(parse_quote!(
785 <#name #type_generics as #behavior::SendSettlements>::InterpretationCustody: ::core::marker::Send
786 ));
787 let (effects_impl, _, effects_where) = effects_generics.split_for_impl();
788 let (logical_impl, _, logical_where) = logical_generics.split_for_impl();
789 let (settlement_impl, _, settlement_where) = settlement_generics.split_for_impl();
790 let (lawful_impl, _, lawful_where) = lawful_generics.split_for_impl();
791 let (interpretation_impl, _, interpretation_where) = interpretation_generics.split_for_impl();
792 let interpretation_loans = fields
793 .iter()
794 .map(|field| {
795 let field_name = field.to_string();
796 format_ident!(
797 "__bombay_{}_interpretation",
798 field_name.trim_start_matches("r#"),
799 span = Span::mixed_site()
800 )
801 })
802 .collect::<Vec<_>>();
803 let loan_fields = interpretation_loans.iter().collect::<Vec<_>>();
804 let remaining_interpretations =
805 Ident::new("__bombay_remaining_interpretations", Span::mixed_site());
806 let interpretation = named_send_interpretation(
807 &loan_fields,
808 field_types,
809 &interpreter,
810 &root_event,
811 &path,
812 behavior,
813 );
814 let source_settlements = field_types
815 .iter()
816 .map(|ty| quote!(<#ty as #behavior::SendSettlements>::Settlements))
817 .collect::<Vec<_>>();
818 let source_custody = field_types
819 .iter()
820 .map(|ty| quote!(<#ty as #behavior::SendSettlements>::SourceCustody))
821 .collect::<Vec<_>>();
822 let (_, source_owner) = source_product_custody(&source_settlements, &source_custody, behavior);
823 let interpretation_custody = field_types
824 .iter()
825 .map(|ty| {
826 quote!(
827 ::core::option::Option<#behavior::InterpretationProgress<
828 #ty,
829 <#ty as #behavior::SendSettlements>::InterpretationCustody,
830 <#ty as #behavior::SendSettlements>::Settlements,
831 >>
832 )
833 })
834 .collect::<Vec<_>>();
835 let completed_controls = fields.iter().map(|_| quote!(::core::option::Option::Some(#behavior::InterpretationProgress::Completed(_)))).collect::<Vec<_>>();
836 let completed_fields = loan_fields
837 .iter()
838 .rev()
839 .fold(quote!(()), |later, field| quote!((#field,#later)));
840 let mut combine = quote!(#behavior::Interpretation::Complete(()));
841 for field in loan_fields.iter().rev() {
843 combine = quote! {
844 match (#field,#combine) {
845 (#behavior::Interpretation::Complete(#field),#behavior::Interpretation::Complete(#remaining_interpretations)) => #behavior::Interpretation::Complete((#field,#remaining_interpretations)),
846 (#behavior::Interpretation::Complete(#field) | #behavior::Interpretation::Corrupt(#field), #behavior::Interpretation::Corrupt(#remaining_interpretations)) |
847 (#behavior::Interpretation::Corrupt(#field),#behavior::Interpretation::Complete(#remaining_interpretations)) => #behavior::Interpretation::Corrupt((#field,#remaining_interpretations)),
848 }
849 };
850 }
851
852 quote! {
853 impl #effects_impl #behavior::SendEffects for #name #type_generics #effects_where {
854 fn empty() -> Self {
855 Self {
856 #(#fields: <#field_types as #behavior::SendEffects>::empty(),)*
857 }
858 }
859
860 fn append(&mut self, other: Self) {
861 #(
862 <#field_types as #behavior::SendEffects>::append(
863 &mut self.#fields,
864 other.#fields,
865 );
866 )*
867 }
868 }
869
870 impl #logical_impl #behavior::LogicalDeliveryProtocols
871 for #name #type_generics #logical_where
872 {
873 type Protocols = #logical_protocols;
874 }
875
876 impl #lawful_impl #behavior::SendsFor<#event>
877 for #name #type_generics #lawful_where
878 {}
879
880 impl #settlement_impl #behavior::SendSettlements
881 for #name #type_generics #settlement_where
882 {
883 type Settlements = #settlement_type;
884 type SourceCustody = #source_owner;
885 type InterpretationCustody = (#(#interpretation_custody,)*);
886
887 fn prepare_interpretation(progress: &mut ::core::option::Option<#behavior::InterpretationProgress<Self,Self::InterpretationCustody,Self::Settlements>>) {
888 match progress.take() {
889 ::core::option::Option::Some(#behavior::InterpretationProgress::Original(original)) => {
890 *progress = ::core::option::Option::Some(#behavior::InterpretationProgress::Interpreting((
891 #(::core::option::Option::Some(#behavior::InterpretationProgress::Original(original.#fields)),)*
892 )));
893 }
894 retained => *progress = retained,
895 }
896 }
897 fn finish_interpretation(progress: &mut ::core::option::Option<#behavior::InterpretationProgress<Self,Self::InterpretationCustody,Self::Settlements>>) {
898 let ::core::option::Option::Some(#behavior::InterpretationProgress::Interpreting(custody)) = progress else { return; };
899 let (#(#loan_fields,)*) = custody;
900 if !matches!((#(&*#loan_fields,)*), (#(#completed_controls,)*)) { return; }
901 match progress.take() {
902 ::core::option::Option::Some(#behavior::InterpretationProgress::Interpreting((
903 #(::core::option::Option::Some(#behavior::InterpretationProgress::Completed(#loan_fields)),)*
904 ))) => {
905 let completed = #combine;
906 *progress = ::core::option::Option::Some(#behavior::InterpretationProgress::Completed(match completed {
907 #behavior::Interpretation::Complete(#completed_fields) => #behavior::Interpretation::Complete(#settlement_name { #(#fields: #loan_fields,)* }),
908 #behavior::Interpretation::Corrupt(#completed_fields) => #behavior::Interpretation::Corrupt(#settlement_name { #(#fields: #loan_fields,)* }),
909 }));
910 }
911 retained => *progress = retained,
912 }
913 }
914 fn unattempted(progress: &mut ::core::option::Option<#behavior::InterpretationProgress<Self,Self::InterpretationCustody,Self::Settlements>>) {
915 <Self as #behavior::SendSettlements>::prepare_interpretation(progress);
916 if let ::core::option::Option::Some(#behavior::InterpretationProgress::Interpreting(custody)) = progress {
917 let (#(#loan_fields,)*) = custody;
918 #(<#field_types as #behavior::SendSettlements>::unattempted(#loan_fields);)*
919 }
920 <Self as #behavior::SendSettlements>::finish_interpretation(progress);
921 }
922 }
923
924 impl #interpretation_impl
925 #behavior::InterpretSends<
926 #interpreter,
927 #root_event,
928 #path,
929 > for #name #type_generics #interpretation_where
930 {
931 fn interpret(
932 progress: &mut ::core::option::Option<#behavior::InterpretationProgress<Self,Self::InterpretationCustody,Self::Settlements>>,
933 interpreter: &mut #interpreter,
934 ) -> impl ::core::future::Future<Output=()> + ::core::marker::Send {
935 async move {
936 #interpretation
937 }
938 }
939 }
940 }
941}
942
943#[allow(
944 clippy::too_many_lines,
945 reason = "each generated contract is one facet of the same named send product"
946)]
947fn derive_send_product_contract(input: DeriveInput) -> Result<TokenStream2> {
948 let behavior = behavior_crate()?;
949 let mut uses = GenericUses::default();
950 uses.visit_derive_input(&input);
951 let send_input = uses.fresh("__BombayInput");
952 let send_path = uses.fresh("__BombaySendPath");
953 let name = input.ident;
954 let generics = input.generics;
955 let Data::Struct(data) = input.data else {
956 return Err(Error::new_spanned(
957 name,
958 "send products must be named structs",
959 ));
960 };
961 let Fields::Named(fields) = data.fields else {
962 return Err(Error::new_spanned(
963 name,
964 "send products need named effect lanes",
965 ));
966 };
967 if fields.named.is_empty() {
968 return Err(Error::new_spanned(
969 name,
970 "empty send products must be omitted",
971 ));
972 }
973 let mut parameters = Vec::new();
974 for parameter in &generics.params {
975 let GenericParam::Type(parameter) = parameter else {
976 return Err(Error::new_spanned(
977 parameter,
978 "generic send products use only lane type parameters",
979 ));
980 };
981 parameters.push(¶meter.ident);
982 }
983 let mut field_names = Vec::new();
984 let mut field_types = Vec::new();
985 for field in &fields.named {
986 let Some(field_name) = field.ident.as_ref() else {
987 return Err(Error::new_spanned(field, "send lanes must be named"));
988 };
989 let Type::Path(path) = &field.ty else {
990 return Err(Error::new_spanned(
991 &field.ty,
992 "each send lane must be one product type parameter",
993 ));
994 };
995 let Some(segment) = path.path.get_ident() else {
996 return Err(Error::new_spanned(
997 &field.ty,
998 "each send lane must be one product type parameter",
999 ));
1000 };
1001 if path.qself.is_some() || !parameters.iter().any(|parameter| *parameter == segment) {
1002 return Err(Error::new_spanned(
1003 &field.ty,
1004 "each send lane must be one product type parameter",
1005 ));
1006 }
1007 field_names.push(field_name);
1008 field_types.push(&field.ty);
1009 }
1010 let (_, type_generics, _) = generics.split_for_impl();
1011 let settled_parameters = parameters
1012 .iter()
1013 .map(|parameter| quote!(<#parameter as #behavior::SendSettlements>::Settlements));
1014 let settlement_type = quote!(#name<#(#settled_parameters),*>);
1015 let send_contract = named_send_contract(
1016 &name,
1017 &generics,
1018 &field_names,
1019 &field_types,
1020 &settlement_type,
1021 &name,
1022 &behavior,
1023 );
1024 let settled_fields = field_types
1025 .iter()
1026 .map(|field_ty| (*field_ty).clone())
1027 .collect::<Vec<_>>();
1028 let settlement_owner = quote!(#name #type_generics);
1029 let settlement_contract = named_settlement_contract(
1030 &name,
1031 &settlement_owner,
1032 &generics,
1033 &field_names,
1034 &settled_fields,
1035 &behavior,
1036 );
1037 let send_inputs =
1038 field_names
1039 .iter()
1040 .zip(&field_types)
1041 .enumerate()
1042 .map(|(index, (field, field_ty))| {
1043 let mut insertion_generics = generics.clone();
1044 insertion_generics.params.push(parse_quote!(#send_input));
1045 insertion_generics.params.push(parse_quote!(#send_path));
1046 insertion_generics
1047 .make_where_clause()
1048 .predicates
1049 .push(parse_quote!(
1050 #field_ty: #behavior::SendInput<#send_input, #send_path>
1051 ));
1052 let (insertion_impl, _, insertion_where) = insertion_generics.split_for_impl();
1053 let path = (0..index).fold(
1056 quote!((#behavior::Own, #send_path)),
1057 |path, _| quote!(#behavior::Inside<#path>),
1058 );
1059 quote! {
1060 impl #insertion_impl #behavior::SendInput<#send_input, #path>
1061 for #name #type_generics #insertion_where
1062 {
1063 fn emit(&mut self, input: #send_input) {
1064 <#field_ty as #behavior::SendInput<
1065 #send_input,
1066 #send_path,
1067 >>::emit(&mut self.#field, input);
1068 }
1069 }
1070 }
1071 });
1072 Ok(quote!(#send_contract #settlement_contract #(#send_inputs)*))
1073}
1074
1075#[proc_macro_derive(SendProduct)]
1086pub fn derive_send_product(input: TokenStream) -> TokenStream {
1087 let input = parse_macro_input!(input as DeriveInput);
1088 derive_send_product_contract(input)
1089 .unwrap_or_else(Error::into_compile_error)
1090 .into()
1091}
1092
1093#[allow(
1094 clippy::too_many_lines,
1095 reason = "the generated nominal product keeps every structural trait implementation adjacent"
1096)]
1097fn generate_sends(
1098 product: Option<SendsSpec>,
1099 actor: &Ident,
1100 item: &ItemImpl,
1101 behavior: &TokenStream2,
1102) -> (TokenStream2, TokenStream2) {
1103 let (product, visibility) = match product {
1104 None => return (quote!(#behavior::NoSends), quote!()),
1105 Some(SendsSpec::Existing(ty)) => return (quote!(#ty), quote!()),
1106 Some(SendsSpec::Generated {
1107 product,
1108 visibility,
1109 }) => (product, visibility),
1110 };
1111 let mut uses = GenericUses::default();
1112 uses.visit_item_impl(item);
1113 for field in &product.fields {
1114 uses.visit_type(&field.ty);
1115 }
1116 let send_input = uses.fresh("__BombayInput");
1117 let address = uses.fresh("__BombayAddress");
1118 let phase = uses.fresh("__BombayPh");
1119 let birth = uses.fresh("__BombayBirth");
1120 let name = format_ident!("{}Sends", actor);
1121 let settlements_name = format_ident!("{}Settlements", actor);
1122 let field_names: Vec<_> = product.fields.iter().map(|field| &field.name).collect();
1123 let field_types: Vec<_> = product.fields.iter().map(|field| &field.ty).collect();
1124 let lane_names: Vec<_> = field_names
1125 .iter()
1126 .map(|field| lane_name(&name, field))
1127 .collect();
1128 let actions_name = format_ident!("{}Actions", actor);
1129 let action_methods: Vec<_> = field_names
1130 .iter()
1131 .map(|field| {
1132 let field = field.to_string();
1133 format_ident!("send_{}", field.trim_start_matches("r#"))
1134 })
1135 .collect();
1136 let product_generics = product_generics(item, &product.fields);
1137 let (_, type_generics, _) = product_generics.split_for_impl();
1138 let generics = &product_generics;
1139 let actor_type = &item.self_ty;
1140 let mut action_trait_generics = product_generics.clone();
1141 action_trait_generics
1142 .make_where_clause()
1143 .predicates
1144 .push(parse_quote!(#actor_type: ::core::marker::Sized));
1145
1146 let action_trait_methods =
1147 action_methods
1148 .iter()
1149 .zip(lane_names.iter())
1150 .map(|(method, lane)| {
1151 quote! {
1152 #[must_use]
1154 fn #method<#send_input>(self, input: #send_input) -> Self
1155 where
1156 Self: #behavior::AppendSend<#send_input, #lane>;
1157 }
1158 });
1159
1160 let action_impl_methods = action_methods
1161 .iter()
1162 .zip(lane_names.iter())
1163 .map(|(method, lane)| {
1164 quote! {
1165 fn #method<#send_input>(self, input: #send_input) -> Self
1166 where
1167 Self: #behavior::AppendSend<#send_input, #lane>,
1168 {
1169 <Self as #behavior::AppendSend<#send_input, #lane>>::append_send(
1170 self,
1171 input,
1172 )
1173 }
1174 }
1175 });
1176
1177 let mut actions_impl_generics = product_generics.clone();
1178 actions_impl_generics.params.push(parse_quote!(#address));
1179 actions_impl_generics.params.push(parse_quote!(#phase));
1180 actions_impl_generics.params.push(parse_quote!(#birth));
1181 actions_impl_generics
1182 .make_where_clause()
1183 .predicates
1184 .push(parse_quote!(#address: #behavior::Address));
1185 actions_impl_generics
1186 .make_where_clause()
1187 .predicates
1188 .push(parse_quote!(#birth: #behavior::BirthMode));
1189 actions_impl_generics
1190 .make_where_clause()
1191 .predicates
1192 .push(parse_quote!(#actor_type: ::core::marker::Sized));
1193 let (actions_impl_generics, _, actions_where_clause) = actions_impl_generics.split_for_impl();
1194
1195 let send_impls = lane_names
1196 .iter()
1197 .zip(field_names.iter())
1198 .zip(field_types.iter())
1199 .map(|((lane, field), field_ty)| {
1200 let mut generics = product_generics.clone();
1201 generics.params.push(parse_quote!(#send_input));
1202 generics.make_where_clause().predicates.push(parse_quote!(
1203 #field_ty: #behavior::SendInput<#send_input, #behavior::Own>
1204 ));
1205 let (impl_generics, _, where_clause) = generics.split_for_impl();
1206 quote! {
1207 impl #impl_generics #behavior::SendInput<#send_input, #lane>
1208 for #name #type_generics #where_clause
1209 {
1210 fn emit(&mut self, input: #send_input) {
1211 <#field_ty as #behavior::SendInput<
1212 #send_input,
1213 #behavior::Own,
1214 >>::emit(&mut self.#field, input);
1215 }
1216 }
1217 }
1218 });
1219
1220 let mut settlement_generics = product_generics.clone();
1221 for field_ty in &field_types {
1222 settlement_generics
1223 .make_where_clause()
1224 .predicates
1225 .push(parse_quote!(#field_ty: #behavior::SendSettlements));
1226 }
1227 let (settlement_impl_generics, settlement_type_generics, settlement_where_clause) =
1228 settlement_generics.split_for_impl();
1229 let settlement_type = quote!(#settlements_name #settlement_type_generics);
1230 let send_contract = named_send_contract(
1231 &name,
1232 &product_generics,
1233 &field_names,
1234 &field_types,
1235 &settlement_type,
1236 &settlements_name,
1237 behavior,
1238 );
1239 let settled_fields = field_types
1240 .iter()
1241 .map(|field_ty| parse_quote!(<#field_ty as #behavior::SendSettlements>::Settlements))
1242 .collect::<Vec<Type>>();
1243 let settlement_contract = named_settlement_contract(
1244 &settlements_name,
1245 &settlement_type,
1246 &settlement_generics,
1247 &field_names,
1248 &settled_fields,
1249 behavior,
1250 );
1251
1252 let items = quote! {
1253 #(#visibility enum #lane_names {})*
1254
1255 #visibility struct #name #generics {
1256 #(pub #field_names: #field_types,)*
1257 }
1258
1259 #visibility struct #settlements_name #settlement_impl_generics #settlement_where_clause {
1260 #(
1261 pub #field_names: <#field_types as #behavior::SendSettlements>::Settlements,
1262 )*
1263 }
1264
1265 #settlement_contract
1266
1267 #visibility trait #actions_name #action_trait_generics: ::core::marker::Sized {
1270 #(#action_trait_methods)*
1271 }
1272
1273 impl #actions_impl_generics #actions_name #type_generics
1274 for #behavior::Actions<
1275 #address,
1276 #phase,
1277 #name #type_generics,
1278 #birth,
1279 >
1280 #actions_where_clause
1281 {
1282 #(#action_impl_methods)*
1283 }
1284
1285 #send_contract
1286
1287 #(#send_impls)*
1288 };
1289 (quote!(#name #type_generics), items)
1290}
1291
1292fn generate_births(
1293 product: Option<BirthsSpec>,
1294 disposition: Option<CreationSettlementDisposition>,
1295 actor: &Ident,
1296 _addr: &Type,
1297 item: &ItemImpl,
1298 behavior: &TokenStream2,
1299) -> (TokenStream2, TokenStream2) {
1300 let product = match product {
1301 None => return (quote!(#behavior::NoBirths), quote!()),
1302 Some(BirthsSpec::Existing(ty)) => {
1303 if disposition.is_none() {
1304 return (quote!(#ty), quote!());
1305 }
1306 let child = quote!(<#ty as #behavior::BirthMode>::Child);
1307 let birth = match disposition {
1308 Some(CreationSettlementDisposition::ReturnToCreator) => {
1309 quote!(#behavior::Births<#child>)
1310 }
1311 Some(CreationSettlementDisposition::RetainForRetirement) => {
1312 quote!(#behavior::RetirementBirths<#child>)
1313 }
1314 None => unreachable!("a no-birth declaration has no settlement disposition"),
1315 };
1316 return (birth, quote!());
1317 }
1318 Some(BirthsSpec::Generated(product)) => product,
1319 };
1320 let name = format_ident!("{}Children", actor);
1321 let roles_name = format_ident!("{}Child", actor);
1322 let role_names = product
1323 .fields
1324 .iter()
1325 .map(|field| lane_name(&name, &field.name))
1326 .collect::<Vec<_>>();
1327 let role_values = product
1328 .fields
1329 .iter()
1330 .map(|field| format_ident!("{}", pascal_name(&field.name)))
1331 .collect::<Vec<_>>();
1332 let declared_child_types = product
1333 .fields
1334 .iter()
1335 .map(|field| &field.ty)
1336 .collect::<Vec<_>>();
1337 let role_positions = (0..product.fields.len())
1338 .map(|index| {
1339 (0..(product.fields.len() - index - 1)).fold(
1340 quote!(#behavior::ChildHead),
1341 |position, _| quote!(#behavior::ChildTail<#position>),
1342 )
1343 })
1344 .collect::<Vec<_>>();
1345 let product_generics = product_generics(item, &product.fields);
1346 let (_, type_generics, _) = product_generics.split_for_impl();
1347 let generics = &product_generics;
1348 let parent = &item.self_ty;
1349 let (parent_impl_generics, _, parent_where_clause) = item.generics.split_for_impl();
1350 let choice = match declared_child_types.as_slice() {
1351 [child] => quote!(#child),
1352 children => children.iter().fold(
1353 quote!(#behavior::Never),
1354 |tail, child| quote!(#behavior::ChildChoice<#child, #tail>),
1355 ),
1356 };
1357 let birth = match disposition {
1358 Some(CreationSettlementDisposition::ReturnToCreator) => {
1359 quote!(#behavior::Births<#name #type_generics>)
1360 }
1361 Some(CreationSettlementDisposition::RetainForRetirement) => {
1362 quote!(#behavior::RetirementBirths<#name #type_generics>)
1363 }
1364 None => unreachable!("a generated birth product has an explicit settlement disposition"),
1365 };
1366 (
1367 birth,
1368 quote! {
1369 pub type #name #generics = #choice;
1370
1371 #(
1372 #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1373 pub struct #role_names;
1374
1375 impl #parent_impl_generics #behavior::ChildRole<#parent> for #role_names
1376 #parent_where_clause
1377 {
1378 type Child = #declared_child_types;
1379 type Position = #role_positions;
1380 }
1381
1382 impl #parent_impl_generics #behavior::ChildOccurrence<#parent> for #role_names
1383 #parent_where_clause
1384 {
1385 type Resolution = #behavior::DeclaredChildOccurrence;
1386 }
1387 )*
1388
1389 pub struct #roles_name;
1390
1391 #[allow(
1392 non_upper_case_globals,
1393 reason = "generated role values retain the application-authored field spelling"
1394 )]
1395 impl #roles_name {
1396 #(pub const #role_values: #role_names = #role_names;)*
1397 }
1398
1399 },
1400 )
1401}
1402
1403#[proc_macro_attribute]
1411#[allow(
1412 clippy::too_many_lines,
1413 reason = "validation and the one coherent Behavior expansion remain in one entry point"
1414)]
1415pub fn behavior(args: TokenStream, item: TokenStream) -> TokenStream {
1416 let args = parse_macro_input!(args as BehaviorArgs);
1417 let item = parse_macro_input!(item as ItemImpl);
1418
1419 if item.trait_.is_some() {
1420 return Error::new_spanned(
1421 &item,
1422 "#[behavior] applies to an inherent impl, not a trait impl",
1423 )
1424 .to_compile_error()
1425 .into();
1426 }
1427
1428 let init = item.items.iter().find_map(|item| match item {
1429 ImplItem::Fn(method) if method.sig.ident == "init" => Some(method),
1430 _ => None,
1431 });
1432 let receive = item.items.iter().find_map(|item| match item {
1433 ImplItem::Fn(method) if method.sig.ident == "receive" => Some(method),
1434 _ => None,
1435 });
1436 let creations_settled = item.items.iter().find_map(|item| match item {
1437 ImplItem::Fn(method) if method.sig.ident == "creations_settled" => Some(method),
1438 _ => None,
1439 });
1440 let Some(receive) = receive else {
1441 return Error::new_spanned(
1442 &item.self_ty,
1443 "#[behavior] requires a receive(&mut self, from, message) method",
1444 )
1445 .to_compile_error()
1446 .into();
1447 };
1448 if let Err(error) = init
1449 .map_or(Ok(()), validate_receiver)
1450 .and_then(|()| validate_receiver(receive))
1451 {
1452 return error.to_compile_error().into();
1453 }
1454 if let Some(init) = init
1455 && init.sig.inputs.len() != 1
1456 {
1457 return Error::new_spanned(&init.sig, "init must accept exactly &mut self")
1458 .to_compile_error()
1459 .into();
1460 }
1461 if receive.sig.inputs.len() != 3 {
1462 return Error::new_spanned(
1463 &receive.sig,
1464 "receive must accept exactly &mut self, from, and message",
1465 )
1466 .to_compile_error()
1467 .into();
1468 }
1469 match (args.creation_settlements, creations_settled) {
1470 (Some(CreationSettlementDisposition::ReturnToCreator), Some(method)) => {
1471 if let Err(error) = validate_receiver(method) {
1472 return error.to_compile_error().into();
1473 }
1474 if method.sig.inputs.len() != 2 {
1475 return Error::new_spanned(
1476 &method.sig,
1477 "creations_settled must accept exactly &mut self and one CreationsSettled value",
1478 )
1479 .to_compile_error()
1480 .into();
1481 }
1482 }
1483 (Some(CreationSettlementDisposition::ReturnToCreator), None) => {
1484 return Error::new_spanned(
1485 &item.self_ty,
1486 "`creation_settlements = return_to_creator` requires a creations_settled transition",
1487 )
1488 .to_compile_error()
1489 .into();
1490 }
1491 (Some(CreationSettlementDisposition::RetainForRetirement), Some(method)) => {
1492 return Error::new_spanned(
1493 &method.sig,
1494 "retirement-owned creation settlements do not enter a creations_settled transition",
1495 )
1496 .to_compile_error()
1497 .into();
1498 }
1499 (Some(CreationSettlementDisposition::RetainForRetirement), None) | (None, None) => {}
1500 (None, Some(_)) => {}
1501 }
1502
1503 let BehaviorArgs {
1504 addr,
1505 message,
1506 sends,
1507 births,
1508 creation_settlements,
1509 error,
1510 } = args;
1511 let self_ty = &item.self_ty;
1512 let Type::Path(self_path) = self_ty.as_ref() else {
1513 return Error::new_spanned(self_ty, "#[behavior] requires a nominal actor type")
1514 .to_compile_error()
1515 .into();
1516 };
1517 let Some(self_name) = self_path.path.segments.last().map(|segment| &segment.ident) else {
1518 return Error::new_spanned(self_ty, "#[behavior] requires a nominal actor type")
1519 .to_compile_error()
1520 .into();
1521 };
1522 let (impl_generics, _, where_clause) = item.generics.split_for_impl();
1523 let behavior = match behavior_crate() {
1524 Ok(behavior) => behavior,
1525 Err(error) => return error.to_compile_error().into(),
1526 };
1527 let error = error.map_or_else(|| quote!(#behavior::Never), |error| quote!(#error));
1528 let initialize = init.map_or_else(
1529 || quote!(::core::result::Result::Ok(#behavior::Actions::cont())),
1530 |_| quote!(<#self_ty>::init(self)),
1531 );
1532
1533 let mut behavior_generics = item.generics.clone();
1534 if let (Some(BirthsSpec::Existing(mode)), None) = (&births, creation_settlements) {
1535 behavior_generics
1536 .make_where_clause()
1537 .predicates
1538 .push(parse_quote!(
1539 #mode: #behavior::BirthMode<Child = #behavior::Never>
1540 ));
1541 }
1542 let (behavior_impl_generics, _, behavior_where_clause) = behavior_generics.split_for_impl();
1543 let (sends_ty, sends_items) = generate_sends(sends, self_name, &item, &behavior);
1544 let (births_ty, births_items) = generate_births(
1545 births,
1546 creation_settlements,
1547 self_name,
1548 &addr,
1549 &item,
1550 &behavior,
1551 );
1552 let (event_ty, transition) = match creation_settlements {
1553 Some(CreationSettlementDisposition::ReturnToCreator) => {
1554 let child = quote!(<#births_ty as #behavior::BirthMode>::Child);
1555 (
1556 quote!(#behavior::CreationEvent<#addr, #child, #message>),
1557 quote! {
1558 match event {
1559 #behavior::CreationEvent::Settlements(settlements) => {
1560 <#self_ty>::creations_settled(self, settlements)
1561 }
1562 #behavior::CreationEvent::User(event) => {
1563 <#self_ty>::receive(self, event.from, event.message)
1564 }
1565 }
1566 },
1567 )
1568 }
1569 Some(CreationSettlementDisposition::RetainForRetirement) | None => (
1570 quote!(#behavior::User<#addr, #message>),
1571 quote!(<#self_ty>::receive(self, event.from, event.message)),
1572 ),
1573 };
1574
1575 quote! {
1576 #sends_items
1577 #births_items
1578 #item
1579
1580 impl #impl_generics #behavior::Protocol for #self_ty #where_clause {
1581 type Addr = #addr;
1582 type Msg = #message;
1583 }
1584
1585 impl #behavior_impl_generics #behavior::Behavior for #self_ty #behavior_where_clause {
1586 type Protocol = Self;
1587 type Event = #event_ty;
1588 type Sends = #sends_ty;
1589 type Ph = #behavior::Never;
1590 type Error = #error;
1591 type Birth = #births_ty;
1592
1593 fn init(
1594 &mut self,
1595 _: #behavior::InitializationTurn,
1596 ) -> #behavior::BehaviorActed<Self> {
1597 #initialize
1598 }
1599
1600 fn transition(
1601 &mut self,
1602 _: #behavior::ActiveTurn,
1603 event: Self::Event,
1604 ) -> #behavior::BehaviorActed<Self> {
1605 #transition
1606 }
1607 }
1608
1609 impl #impl_generics #behavior::BehaviorBase for #self_ty #where_clause {
1610 type Base = Self;
1611
1612 fn base(&self) -> &Self {
1613 self
1614 }
1615 }
1616 }
1617 .into()
1618}
1619
1620#[proc_macro_attribute]
1628pub fn pool_worker(args: TokenStream, item: TokenStream) -> TokenStream {
1629 let PoolWorkerArgs { addr, result } = parse_macro_input!(args as PoolWorkerArgs);
1630 let mut item = parse_macro_input!(item as ItemImpl);
1631
1632 if item.trait_.is_some() {
1633 return Error::new_spanned(
1634 &item,
1635 "#[pool_worker] applies to an inherent impl, not a trait impl",
1636 )
1637 .to_compile_error()
1638 .into();
1639 }
1640
1641 let self_ty = item.self_ty.clone();
1642 if !matches!(self_ty.as_ref(), Type::Path(path) if path.qself.is_none()) {
1643 return Error::new_spanned(&self_ty, "#[pool_worker] requires a nominal worker type")
1644 .to_compile_error()
1645 .into();
1646 }
1647
1648 let actors = match actors_crate() {
1649 Ok(actors) => actors,
1650 Err(error) => return error.to_compile_error().into(),
1651 };
1652 let behavior = match behavior_crate() {
1653 Ok(behavior) => behavior,
1654 Err(error) => return error.to_compile_error().into(),
1655 };
1656 let assignment = {
1657 let mut transitions = item.items.iter_mut().filter_map(|item| match item {
1658 ImplItem::Fn(method) if method.sig.ident == "transition" => Some(method),
1659 _ => None,
1660 });
1661 let Some(transition) = transitions.next() else {
1662 return Error::new_spanned(
1663 &self_ty,
1664 "#[pool_worker] requires transition(&mut self, Assignment<Job>)",
1665 )
1666 .to_compile_error()
1667 .into();
1668 };
1669 if let Some(duplicate) = transitions.next() {
1670 return Error::new_spanned(
1671 &duplicate.sig,
1672 "#[pool_worker] accepts exactly one transition method",
1673 )
1674 .to_compile_error()
1675 .into();
1676 }
1677 let assignment = match pool_worker_assignment(transition) {
1678 Ok(assignment) => assignment,
1679 Err(error) => return error.to_compile_error().into(),
1680 };
1681 transition.sig.output = parse_quote!(-> #behavior::BehaviorActed<Self>);
1682 assignment
1683 };
1684 let (impl_generics, _, where_clause) = item.generics.split_for_impl();
1685
1686 quote! {
1687 #item
1688
1689 impl #impl_generics #behavior::Protocol for #self_ty #where_clause {
1690 type Addr = #addr;
1691 type Msg = #assignment;
1692 }
1693
1694 impl #impl_generics #behavior::Behavior for #self_ty #where_clause {
1695 type Protocol = Self;
1696 type Event = #behavior::User<#addr, #assignment>;
1697 type Sends = #behavior::InterpreterRequests<
1698 #behavior::ReportToParent<#actors::pool::Completion<#result>>
1699 >;
1700 type Ph = #behavior::Never;
1701 type Error = #behavior::Never;
1702 type Birth = #behavior::NoBirths;
1703
1704 fn transition(
1705 &mut self,
1706 _: #behavior::ActiveTurn,
1707 event: Self::Event,
1708 ) -> #behavior::BehaviorActed<Self> {
1709 <#self_ty>::transition(self, event.message)
1710 }
1711 }
1712
1713 impl #impl_generics #behavior::BehaviorBase for #self_ty #where_clause {
1714 type Base = Self;
1715
1716 fn base(&self) -> &Self {
1717 self
1718 }
1719 }
1720 }
1721 .into()
1722}
1723
1724#[cfg(test)]
1725mod tests {
1726 use super::*;
1727
1728 fn permutations(values: &mut [&str], at: usize, output: &mut Vec<String>) {
1729 if at == values.len() {
1730 output.push(values.join(", "));
1731 return;
1732 }
1733 for index in at..values.len() {
1734 values.swap(at, index);
1735 permutations(values, at + 1, output);
1736 values.swap(at, index);
1737 }
1738 }
1739
1740 #[test]
1741 fn every_argument_order_parses() {
1742 let mut declarations = [
1743 "addr = u64",
1744 "message = String",
1745 "sends = { first: Vec<u8>, second: Vec<u16> }",
1746 "births = { first: u8, second: u16 }",
1747 "creation_settlements = retain_for_retirement",
1748 "error = String",
1749 ];
1750 let mut cases = Vec::new();
1751 permutations(&mut declarations, 0, &mut cases);
1752 assert_eq!(cases.len(), 720);
1753 for case in cases {
1754 syn::parse_str::<BehaviorArgs>(&case)
1755 .unwrap_or_else(|error| panic!("failed to parse `{case}`: {error}"));
1756 }
1757 }
1758
1759 #[test]
1760 fn capability_omission_and_trailing_comma_parse() {
1761 syn::parse_str::<BehaviorArgs>("message = (), addr = u8,")
1762 .expect("only required protocol declarations are sufficient");
1763 for declaration in ["NoBirths", "behavior::NoBirths", "EmptyBirths"] {
1764 syn::parse_str::<BehaviorArgs>(&format!(
1765 "message = (), addr = u8, births = {declaration}"
1766 ))
1767 .unwrap_or_else(|error| panic!("explicit no-birth declaration did not parse: {error}"));
1768 }
1769 }
1770
1771 #[test]
1772 fn invalid_declaration_states_are_rejected() {
1773 for case in [
1774 "message = ()",
1775 "addr = u8",
1776 "addr = u8, message = (), addr = u16",
1777 "addr = u8, message = (), unknown = u8",
1778 "addr = u8, message = (), sends = {}",
1779 "addr = u8, message = (), births = {}",
1780 "addr = u8, message = (), births = {}, creation_settlements = retain_for_retirement",
1781 "addr = u8, message = (), sends = { same: Vec<u8>, same: Vec<u8> }",
1782 "addr = u8, message = (), births = { same: u8, same: u16 }, creation_settlements = retain_for_retirement",
1783 "addr = u8, message = (), creation_settlements = retain_for_retirement",
1784 "addr = u8, message = (), births = { child: u8 }, creation_settlements = discard",
1785 "addr = u8, message = (), births = { child: u8 }, creation_settlements = retain_for_retirement, creation_settlements = return_to_creator",
1786 ] {
1787 assert!(
1788 syn::parse_str::<BehaviorArgs>(case).is_err(),
1789 "invalid declaration parsed: `{case}`"
1790 );
1791 }
1792 }
1793
1794 #[test]
1795 fn pool_worker_arguments_require_one_address_and_result() {
1796 for valid in [
1797 "addr = u64, result = String",
1798 "result = String, addr = u64,",
1799 ] {
1800 syn::parse_str::<PoolWorkerArgs>(valid)
1801 .unwrap_or_else(|error| panic!("failed to parse `{valid}`: {error}"));
1802 }
1803 for invalid in [
1804 "addr = u64",
1805 "result = String",
1806 "addr = u64, result = String, addr = u32",
1807 "addr = u64, result = String, result = u8",
1808 "addr = u64, result = String, message = u8",
1809 ] {
1810 assert!(
1811 syn::parse_str::<PoolWorkerArgs>(invalid).is_err(),
1812 "invalid pool worker arguments parsed: `{invalid}`"
1813 );
1814 }
1815 }
1816
1817 #[test]
1818 fn pool_worker_transition_requires_the_domain_source_shape() {
1819 let valid: ItemImpl = syn::parse_str(
1820 "impl Worker { fn transition(&mut self, assignment: Assignment<Job>) \
1821 -> WorkerActed<Self> { result() } }",
1822 )
1823 .expect("valid inherent worker declaration parses");
1824 let method = valid
1825 .items
1826 .iter()
1827 .find_map(|item| match item {
1828 ImplItem::Fn(method) => Some(method),
1829 _ => None,
1830 })
1831 .expect("fixture declares one method");
1832 let assignment = pool_worker_assignment(method)
1833 .unwrap_or_else(|error| panic!("valid worker transition rejected: {error}"));
1834 assert_eq!(quote!(#assignment).to_string(), "Assignment < Job >");
1835
1836 for invalid in [
1837 "impl Worker { fn transition(self, assignment: Assignment<Job>) -> WorkerActed<Self> { result() } }",
1838 "impl Worker { async fn transition(&mut self, assignment: Assignment<Job>) -> WorkerActed<Self> { result() } }",
1839 "impl Worker { fn transition(&mut self, job: Job) -> WorkerActed<Self> { result() } }",
1840 "impl Worker { fn transition(&mut self, assignment: Assignment<Job>) -> WorkerActed<Job> { result() } }",
1841 "impl Worker { fn transition(&mut self, assignment: Assignment<Job>) -> Result<(), ()> { result() } }",
1842 ] {
1843 let declaration: ItemImpl = syn::parse_str(invalid)
1844 .unwrap_or_else(|error| panic!("invalid fixture did not parse: {error}"));
1845 let method = declaration
1846 .items
1847 .iter()
1848 .find_map(|item| match item {
1849 ImplItem::Fn(method) => Some(method),
1850 _ => None,
1851 })
1852 .expect("fixture declares one method");
1853 assert!(
1854 pool_worker_assignment(method).is_err(),
1855 "invalid pool worker transition was accepted: `{invalid}`"
1856 );
1857 }
1858 }
1859}