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::pool`), 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
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    // Raw and ordinary spellings name the same Rust identifier. Also avoid
488    // references: a new parameter must not capture an authored type alias.
489    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(&parameter.lifetime.ident.to_string()),
522            GenericParam::Type(parameter) => {
523                uses.identifiers.contains(&parameter.ident.to_string())
524            }
525            GenericParam::Const(parameter) => {
526                uses.identifiers.contains(&parameter.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            // Only the guaranteed-Send child future crosses this await.
553            // Its input/reply owners and every sibling remain in parent progress.
554            {
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            // All direct child loans below are synchronous and end before
560            // constructing the next child producer.
561            {
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                // The concrete Core tuple prepare only decomposes; it calls no child method.
674                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    // This pure structural fold invokes no field/user method and crosses no await.
842    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(&parameter.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                // Disjoint existing witnesses identify a field without changing the
1054                // field's own semantic lane or choosing between duplicate inputs.
1055                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/// Derive the ordered send and settlement contracts for a named generic
1076/// product whose fields are its lane type parameters.
1077///
1078/// Each field also forwards its existing typed SendInput capability. Unique
1079/// inputs infer their lane through nested products and wrappers; duplicate
1080/// capabilities remain ambiguous. Insertion moves one original input into one
1081/// lane, retaining all other fields and their ordering. Nominal actor products
1082/// keep their separately generated semantic lane contracts. Caller parameter
1083/// names, including raw identifiers, need no reserved implementation prefix;
1084/// generated private parameters avoid declarations and referenced type names.
1085#[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                    /// Append one value to this generated semantic send lane.
1153                    #[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        /// Fluent, statically routed send-lane methods for this behavior's
1268        /// generated action product.
1269        #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/// Generate the mechanical `Behavior` implementation for a normal inherent
1404/// impl containing `receive(&mut self, from, message)` and, optionally,
1405/// `init(&mut self)`. Omitting `init` selects the behavior algebra's empty
1406/// initialization transition. The original impl and methods are preserved
1407/// unchanged. Generated send contracts preserve caller parameter and referenced
1408/// type names; public product and lane names remain derived from the actor.
1409///
1410#[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/// Derive one pool worker's ordinary actor protocol and behavior from its
1621/// assignment transition.
1622///
1623/// This attribute owns syntax only. The generated send product is the existing
1624/// typed parent request carrying the opaque completion produced by
1625/// `Assignment::complete`; assignment authority and delivery remain owned by
1626/// the Behavior Actors pool protocol and the runtime interpreter.
1627#[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}