Skip to main content

behavior_macros/
lib.rs

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