typhoon-context-macro 0.1.0-alpha.12

TODO
Documentation
use {
    super::GeneratorResult,
    crate::{accounts::Account, context::Context, visitor::ContextVisitor, StagedGenerator},
    proc_macro2::TokenStream,
    quote::quote,
    typhoon_syn::constraints::{ConstraintInit, ConstraintInitIfNeeded},
};

struct AccountGenerator<'a> {
    account: &'a Account,
    has_init: bool,
    has_init_if_needed: bool,
}

impl<'a> AccountGenerator<'a> {
    pub fn new(account: &'a Account) -> Self {
        Self {
            account,
            has_init: false,
            has_init_if_needed: false,
        }
    }

    pub fn generate(&self) -> Result<TokenStream, syn::Error> {
        let name = &self.account.name;
        let name_str = name.to_string();

        let assign = if self.has_init {
            quote! {
                <Mut<SystemAccount> as FromAccountInfo>::try_from_info(#name).trace_account(#name_str)?
            }
        } else if self.has_init_if_needed {
            quote! {
                <Mut<UncheckedAccount> as FromAccountInfo>::try_from_info(#name).trace_account(#name_str)?
            }
        } else {
            let account_ty = &self.account.ty;
            quote! {
                <#account_ty as FromAccountInfo>::try_from_info(#name).trace_account(#name_str)?
            }
        };

        if self.account.is_optional {
            Ok(quote! {
                let #name = if #name.key() == program_id {
                    None
                } else {
                    Some(#assign)
                };
            })
        } else {
            Ok(quote!(let #name = #assign;))
        }
    }
}

impl ContextVisitor for AccountGenerator<'_> {
    fn visit_init(&mut self, _constraint: &ConstraintInit) -> Result<(), syn::Error> {
        self.has_init = true;

        Ok(())
    }

    fn visit_init_if_needed(
        &mut self,
        _constraint: &ConstraintInitIfNeeded,
    ) -> Result<(), syn::Error> {
        self.has_init_if_needed = true;

        Ok(())
    }
}

pub struct AssignGenerator<'a>(&'a Context);

impl<'a> AssignGenerator<'a> {
    pub fn new(context: &'a Context) -> Self {
        AssignGenerator(context)
    }
}

impl StagedGenerator for AssignGenerator<'_> {
    fn append(&mut self, context: &mut GeneratorResult) -> Result<(), syn::Error> {
        for account in &self.0.accounts {
            let mut generator = AccountGenerator::new(account);
            generator.visit_account(account)?;
            context.inside.extend(generator.generate()?);
        }

        Ok(())
    }
}