statum-macros 0.9.0

Proc macros for representing legal workflow and protocol states explicitly in Rust
Documentation
use proc_macro2::TokenStream;
use quote::{format_ident, quote};

use crate::machine::machine_type_with_state;
use crate::state::{ParsedEnumInfo, ParsedVariantInfo};

use super::super::metadata::{ParsedMachineInfo, is_rust_analyzer};
use super::builder::{
    BuilderContext, machine_struct_initialization, rust_analyzer_builder_tokens,
    typestate_builder_tokens, variant_payload_type,
};
use crate::machine::MachineInfo;

impl MachineInfo {
    pub fn generate_builder_methods(
        &self,
        parsed_machine: &ParsedMachineInfo,
        parsed_state: &ParsedEnumInfo,
    ) -> TokenStream {
        let parsed_fields = parsed_machine.field_idents_and_types();
        let field_names = parsed_fields
            .iter()
            .map(|(field_ident, _)| field_ident.clone())
            .collect::<Vec<_>>();
        let field_types = parsed_fields
            .iter()
            .map(|(_, field_ty)| field_ty.clone())
            .collect::<Vec<_>>();

        let machine_ident = format_ident!("{}", self.name);
        let builder_context = BuilderContext {
            machine_ident: &machine_ident,
            machine_generics: &parsed_machine.generics,
            builder_vis: &parsed_machine.vis,
            field_names: &field_names,
            field_types: &field_types,
            use_ra_shim: is_rust_analyzer(),
        };
        let builder_methods = parsed_state
            .variants
            .iter()
            .map(|variant| generate_variant_builder_tokens(&builder_context, variant));

        quote! {
            #(#builder_methods)*
        }
    }
}

fn generate_variant_builder_tokens(
    context: &BuilderContext<'_>,
    variant: &ParsedVariantInfo,
) -> TokenStream {
    let variant_ident = format_ident!("{}", variant.name);
    let variant_builder_ident = format_ident!("{}{}Builder", context.machine_ident, variant.name);
    let data_type = variant_payload_type(variant);
    generate_custom_builder_tokens(
        context,
        &variant_ident,
        &variant_builder_ident,
        data_type.as_ref(),
    )
}

fn generate_custom_builder_tokens(
    context: &BuilderContext<'_>,
    variant_ident: &syn::Ident,
    variant_builder_ident: &syn::Ident,
    data_type: Option<&syn::Type>,
) -> TokenStream {
    let machine_ident = context.machine_ident;
    let machine_state_ty = machine_type_with_state(
        quote! { #machine_ident },
        context.machine_generics,
        quote! { #variant_ident },
    );
    let struct_initialization = machine_struct_initialization(context, data_type.is_some());

    if context.use_ra_shim {
        rust_analyzer_builder_tokens(context, variant_builder_ident, data_type, &machine_state_ty)
    } else {
        typestate_builder_tokens(
            context,
            variant_builder_ident,
            data_type,
            &machine_state_ty,
            struct_initialization,
        )
    }
}