depcon_codegen 0.3.0

Code generation macros for depcon dependency injection framework
Documentation
use crate::{attribute_provide, utils::import_crate};
use proc_macro2::TokenStream;
use quote::quote;
use syn::ItemImpl;

pub fn transform(attribute: &TokenStream, input: ItemImpl) -> TokenStream {
    let base = attribute_provide::transform(attribute, input);
    assert!(
        base.generics.params.is_empty(),
        "Only concrete types can be registered. Use auto_register! instead"
    );

    let crate_path = import_crate();

    let provider_type = base.provider_type;
    let service_type = base.service_type;

    let register = quote! {
        #crate_path::auto_register!(#provider_type, #service_type);
    };

    TokenStream::from_iter([base.output, register])
}

#[cfg(test)]
mod test {
    use super::*;
    use quote::quote;
    use syn::parse2;

    #[test]
    fn test_auto_provide() {
        let input = quote! {
            impl some::Trait for some::Struct {}
        };
        let input: ItemImpl = parse2(input).unwrap();
        let attribute = TokenStream::new();
        let actual = transform(&attribute, input).to_string();
        let expected = quote! {
            impl some::Trait for some::Struct {}
            impl depcon::Provider<dyn some::Trait> for some::Struct {
                fn provide(self: std::sync::Arc<Self>) -> std::sync::Arc<dyn some::Trait> {
                    self
                }
            }
            depcon::auto_register!(some::Struct, dyn some::Trait);
        }
        .to_string();

        assert_eq!(actual, expected);
    }

    #[test]
    #[should_panic(expected = "attribute does not accept parameters")]
    fn test_reject_params() {
        let input = quote! {
            impl Trait for Struct {}
        };
        let input: ItemImpl = parse2(input).unwrap();
        let attribute = quote! { bad };
        transform(&attribute, input);
    }
}