imbibe-macros 0.0.1

contains the proc macros used by imbibe components
Documentation
use proc_macro::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{DeriveInput, Ident, Token, parse_macro_input};

struct SignerFields {
	fields: Vec<Ident>,
}

#[proc_macro_derive(GetSigners, attributes(signer_fields))]
pub fn derive_get_signers(input: TokenStream) -> TokenStream {
	let input = parse_macro_input!(input as DeriveInput);
	let struct_name = &input.ident;

	let signer_fields = input
		.attrs
		.iter()
		.find_map(|attr| {
			attr.path()
				.is_ident("signer_fields")
				.then(|| attr.parse_args::<SignerFields>().ok().map(|sf| sf.fields))
				.flatten()
		})
		.unwrap_or_default();

	let mut borrowed_field_exprs = vec![];
	let mut owned_field_exprs = vec![];
	if let syn::Data::Struct(data_struct) = &input.data {
		for field_name in signer_fields {
			let field = data_struct
				.fields
				.iter()
				.find(|f| f.ident.as_ref().map(|id| id == &field_name).unwrap_or(false));

			if let Some(field) = field {
				let field_ident = field.ident.as_ref().unwrap();
				let ty = &field.ty;
				let ty_str = quote! { #ty }.to_string().replace(' ', "");

				let (borrowed_field_expr, owned_field_expr) = match ty_str.as_str() {
					"::prost::alloc::string::String" => (
						quote! { .chain(std::iter::once(self.#field_ident.as_str())) },
						quote! { .chain(std::iter::once(self.#field_ident)) },
					),
					"::prost::alloc::vec::Vec<::prost::alloc::string::String>" => (
						quote! { .chain(self.#field_ident.iter().map(|s| s.as_str())) },
						quote! { .chain(self.#field_ident) },
					),
					s if s.starts_with("::prost::alloc::vec::Vec<") => (
						quote! { .chain(self.#field_ident.iter().flat_map(GetSigners::signers)) },
						quote! { .chain(self.#field_ident.into_iter().flat_map(GetSigners::signers)) },
					),
					_ => (
						quote! { .chain(GetSigners::singers(&self.#field_ident)) },
						quote! { .chain(GetSigners::singers(self.#field_ident)) },
					),
				};

				borrowed_field_exprs.push(borrowed_field_expr);
				owned_field_exprs.push(owned_field_expr);
			}
		}
	}

	let expanded = quote! {
		impl<'a> GetSigners for &'a #struct_name {
			type Signer = &'a str;

			fn signers(self) -> impl Iterator<Item = Self::Signer> {
				std::iter::empty()
					#(#borrowed_field_exprs)*
			}
		}


		impl GetSigners for #struct_name {
			type Signer = String;

			fn signers(self) -> impl Iterator<Item = Self::Signer> {
				std::iter::empty()
					#(#owned_field_exprs)*
			}
		}
	};

	TokenStream::from(expanded)
}

impl Parse for SignerFields {
	fn parse(input: ParseStream) -> syn::Result<Self> {
		let mut fields = vec![];
		while !input.is_empty() {
			fields.push(input.parse()?);
			if input.peek(Token![,]) {
				input.parse::<Token![,]>()?;
			}
		}

		Ok(SignerFields { fields })
	}
}