From 5335cdad99a6d566ea6e83f97012c7954ba93c45 Mon Sep 17 00:00:00 2001 From: HampusM Date: Sun, 24 Jul 2022 15:58:30 +0200 Subject: feat: add support for generics --- README.md | 2 +- examples/generics/bootstrap.rs | 17 +++++++++++++++++ examples/generics/interfaces/mod.rs | 1 + examples/generics/interfaces/printer.rs | 6 ++++++ examples/generics/main.rs | 19 +++++++++++++++++++ examples/generics/printer.rs | 24 ++++++++++++++++++++++++ macros/src/injectable_impl.rs | 15 +++++++++++---- macros/src/lib.rs | 25 +++++++++++++++++++------ src/lib.rs | 17 +++++++++++++++++ 9 files changed, 115 insertions(+), 11 deletions(-) create mode 100644 examples/generics/bootstrap.rs create mode 100644 examples/generics/interfaces/mod.rs create mode 100644 examples/generics/interfaces/printer.rs create mode 100644 examples/generics/main.rs create mode 100644 examples/generics/printer.rs diff --git a/README.md b/README.md index d801210..7c829e4 100644 --- a/README.md +++ b/README.md @@ -103,5 +103,5 @@ fn main() For more examples see the [examples folder](https://git.hampusmat.com/syrette/tree/examples). ## Todo -- Add support for generics +- Add support generic factories diff --git a/examples/generics/bootstrap.rs b/examples/generics/bootstrap.rs new file mode 100644 index 0000000..072350e --- /dev/null +++ b/examples/generics/bootstrap.rs @@ -0,0 +1,17 @@ +use syrette::{di_container_bind, DIContainer}; + +// Interfaces +use crate::interfaces::printer::IPrinter; +// +// Implementations +use crate::printer::Printer; + +pub fn bootstrap() -> DIContainer +{ + let mut di_container = DIContainer::new(); + + di_container_bind!(IPrinter => Printer, di_container); + di_container_bind!(IPrinter => Printer, di_container); + + di_container +} diff --git a/examples/generics/interfaces/mod.rs b/examples/generics/interfaces/mod.rs new file mode 100644 index 0000000..3f31bdf --- /dev/null +++ b/examples/generics/interfaces/mod.rs @@ -0,0 +1 @@ +pub mod printer; diff --git a/examples/generics/interfaces/printer.rs b/examples/generics/interfaces/printer.rs new file mode 100644 index 0000000..3098d0b --- /dev/null +++ b/examples/generics/interfaces/printer.rs @@ -0,0 +1,6 @@ +use std::fmt::Display; + +pub trait IPrinter +{ + fn print(&self, out: Printable); +} diff --git a/examples/generics/main.rs b/examples/generics/main.rs new file mode 100644 index 0000000..9442641 --- /dev/null +++ b/examples/generics/main.rs @@ -0,0 +1,19 @@ +mod bootstrap; +mod interfaces; +mod printer; + +use bootstrap::bootstrap; +use interfaces::printer::IPrinter; + +fn main() +{ + let di_container = bootstrap(); + + let string_printer = di_container.get::>().unwrap(); + + string_printer.print("Hello there".to_string()); + + let int_printer = di_container.get::>().unwrap(); + + int_printer.print(2782028); +} diff --git a/examples/generics/printer.rs b/examples/generics/printer.rs new file mode 100644 index 0000000..4307c4f --- /dev/null +++ b/examples/generics/printer.rs @@ -0,0 +1,24 @@ +use std::fmt::Display; + +use syrette::injectable; + +use crate::interfaces::printer::IPrinter; + +pub struct Printer {} + +#[injectable] +impl Printer +{ + pub fn new() -> Self + { + Self {} + } +} + +impl IPrinter for Printer +{ + fn print(&self, out: Printable) + { + println!("{}", out); + } +} diff --git a/macros/src/injectable_impl.rs b/macros/src/injectable_impl.rs index 8bb6c7a..822a432 100644 --- a/macros/src/injectable_impl.rs +++ b/macros/src/injectable_impl.rs @@ -1,5 +1,6 @@ use quote::{quote, ToTokens}; use syn::parse::{Parse, ParseStream}; +use syn::Generics; use syn::{ parse_str, punctuated::Punctuated, token::Comma, ExprMethodCall, FnArg, GenericArgument, Ident, ImplItem, ImplItemMethod, ItemImpl, Path, PathArguments, @@ -12,6 +13,7 @@ pub struct InjectableImpl { pub dependency_types: Vec, pub self_type: Type, + pub generics: Generics, pub original_impl: ItemImpl, } @@ -25,6 +27,7 @@ impl Parse for InjectableImpl Ok(dependency_types) => Ok(Self { dependency_types, self_type: impl_parsed_input.self_ty.as_ref().clone(), + generics: impl_parsed_input.generics.clone(), original_impl: impl_parsed_input, }), Err(error_msg) => Err(input.error(error_msg)), @@ -39,17 +42,21 @@ impl InjectableImpl { pub fn expand(&self) -> proc_macro2::TokenStream { - let original_impl = &self.original_impl; - let self_type = &self.self_type; + let Self { + dependency_types, + self_type, + generics, + original_impl, + } = self; let di_container_var: Ident = parse_str(DI_CONTAINER_VAR_NAME).unwrap(); - let get_dependencies = Self::_create_get_dependencies(&self.dependency_types); + let get_dependencies = Self::_create_get_dependencies(dependency_types); quote! { #original_impl - impl syrette::interfaces::injectable::Injectable for #self_type { + impl #generics syrette::interfaces::injectable::Injectable for #self_type { fn resolve( #di_container_var: &syrette::DIContainer ) -> syrette::libs::error_stack::Result< diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 73fb2dc..d65df68 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -19,27 +19,40 @@ use libs::intertrait_macros::gen_caster::generate_caster; /// Makes a struct injectable. Thereby usable with `DIContainer`. /// /// # Arguments -/// * A interface trait the struct implements. +/// * (Optional) A interface trait the struct implements. /// /// # Panics /// If the attributed item is not a impl. +/// +/// # Important +/// If the interface trait argument is excluded, you should either manually +/// declare the interface with the `declare_interface` macro or use +/// the `di_container_bind` macro to create a DI container binding. #[proc_macro_attribute] pub fn injectable(args_stream: TokenStream, impl_stream: TokenStream) -> TokenStream { - let InjectableMacroArgs { - interface: interface_type_path, - } = parse_macro_input!(args_stream); + let should_declare_interface = !args_stream.is_empty(); let injectable_impl: InjectableImpl = parse(impl_stream).unwrap(); let expanded_injectable_impl = injectable_impl.expand(); - let self_type = &injectable_impl.self_type; + let maybe_decl_interface = if should_declare_interface { + let InjectableMacroArgs { interface } = parse_macro_input!(args_stream); + + let self_type = &injectable_impl.self_type; + + quote! { + syrette::declare_interface!(#self_type -> #interface); + } + } else { + quote! {} + }; quote! { #expanded_injectable_impl - syrette::declare_interface!(#self_type -> #interface_type_path); + #maybe_decl_interface } .into() } diff --git a/src/lib.rs b/src/lib.rs index 5724f10..cb0dd5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,3 +22,20 @@ pub mod libs; // Private mod provider; + +/// Shortcut for creating a DI container binding for a injectable without a declared interface. +/// +/// This will declare a interface for the implementation. +/// +/// Useful for when the implementation or the interface is generic. +/// +/// # Arguments +/// {interface} => {implementation}, {DI container variable name} +#[macro_export] +macro_rules! di_container_bind { + ($interface: path => $implementation: ty, $di_container: ident) => { + $di_container.bind::().to::<$implementation>(); + + syrette::declare_interface!($implementation -> $interface); + }; +} -- cgit v1.2.3-18-g5258