aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2022-07-24 15:58:30 +0200
committerHampusM <hampus@hampusmat.com>2022-07-24 15:58:30 +0200
commit5335cdad99a6d566ea6e83f97012c7954ba93c45 (patch)
treeb4c91634b34f6d3858d6dd210afb609d9a1f76d7
parent5ea16d1e5bf716831bc8c54c5c4c86229d7e0463 (diff)
feat: add support for generics
-rw-r--r--README.md2
-rw-r--r--examples/generics/bootstrap.rs17
-rw-r--r--examples/generics/interfaces/mod.rs1
-rw-r--r--examples/generics/interfaces/printer.rs6
-rw-r--r--examples/generics/main.rs19
-rw-r--r--examples/generics/printer.rs24
-rw-r--r--macros/src/injectable_impl.rs15
-rw-r--r--macros/src/lib.rs25
-rw-r--r--src/lib.rs17
9 files changed, 115 insertions, 11 deletions
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<String> => Printer, di_container);
+ di_container_bind!(IPrinter<i32> => 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<Printable: Display>
+{
+ 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::<dyn IPrinter<String>>().unwrap();
+
+ string_printer.print("Hello there".to_string());
+
+ let int_printer = di_container.get::<dyn IPrinter<i32>>().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<Printable: Display> IPrinter<Printable> 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<Type>,
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::<dyn $interface>().to::<$implementation>();
+
+ syrette::declare_interface!($implementation -> $interface);
+ };
+}