diff --git a/guide/src/guide/links.md b/guide/src/guide/links.md index b43d7c7..9e067bf 100644 --- a/guide/src/guide/links.md +++ b/guide/src/guide/links.md @@ -8,92 +8,92 @@ REF: https://github.com/rust-lang/cargo/issues/739 REF: https://github.com/tag1consulting/goose/issues/320 --> -[`Ref`]: https://docs.rs/more-di/3.0.0/di/type.Ref.html -[`Type`]: https://docs.rs/more-di/3.0.0/di/struct.Type.html -[`ServiceCardinality`]: https://docs.rs/more-di/3.0.0/di/enum.ServiceCardinality.html -[`ServiceLifetime`]: https://docs.rs/more-di/3.0.0/di/enum.ServiceLifetime.html -[`ServiceDependency`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceDependency.html -[`ServiceDescriptor`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceDescriptor.html +[`Ref`]: https://docs.rs/more-di/3.1.0/di/type.Ref.html +[`Type`]: https://docs.rs/more-di/3.1.0/di/struct.Type.html +[`ServiceCardinality`]: https://docs.rs/more-di/3.1.0/di/enum.ServiceCardinality.html +[`ServiceLifetime`]: https://docs.rs/more-di/3.1.0/di/enum.ServiceLifetime.html +[`ServiceDependency`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceDependency.html +[`ServiceDescriptor`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceDescriptor.html -[`ServiceCollection`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceCollection.html -[`ServiceCollection::build_provider()`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceCollection.html#method.build_provider -[`add`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceCollection.html#method.add -[`try_add`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceCollection.html#method.try_add -[`try_add_to_all`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceCollection.html#method.try_add_to_all -[`try_add_all`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceCollection.html#method.try_add_all -[`replace`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceCollection.html#method.replace -[`try_replace`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceCollection.html#method.try_replace +[`ServiceCollection`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceCollection.html +[`ServiceCollection::build_provider()`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceCollection.html#method.build_provider +[`add`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceCollection.html#method.add +[`try_add`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceCollection.html#method.try_add +[`try_add_to_all`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceCollection.html#method.try_add_to_all +[`try_add_all`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceCollection.html#method.try_add_all +[`replace`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceCollection.html#method.replace +[`try_replace`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceCollection.html#method.try_replace -[`ValidationError`]: https://docs.rs/more-di/3.0.0/di/struct.ValidationError.html -[`validate`]: https://docs.rs/more-di/3.0.0/di/fn.validate.html +[`ValidationError`]: https://docs.rs/more-di/3.1.0/di/struct.ValidationError.html +[`validate`]: https://docs.rs/more-di/3.1.0/di/fn.validate.html -[`ServiceProvider`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceProvider.html -[`ScopedServiceProvider`]: https://docs.rs/more-di/3.0.0/di/struct.ScopedServiceProvider.html -[`create_scope`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceProvider.html#method.create_scope -[`get`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceProvider.html#method.get -[`get_mut`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceProvider.html#method.get_mut -[`get_by_key`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceProvider.html#method.get_by_key -[`get_by_key_mut`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceProvider.html#method.get_by_key_mut -[`get_all`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceProvider.html#method.get_all -[`get_all_mut`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceProvider.html#method.get_all_mut -[`get_all_by_key`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceProvider.html#method.get_all_by_key -[`get_all_by_key_mut`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceProvider.html#method.get_all_by_key_mut -[`get_required`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceProvider.html#method.get_required -[`get_required_mut`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceProvider.html#method.get_required_mut -[`get_required_by_key`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceProvider.html#method.get_required_by_key -[`get_required_by_key_mut`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceProvider.html#method.get_required_by_key_mut +[`ServiceProvider`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceProvider.html +[`ScopedServiceProvider`]: https://docs.rs/more-di/3.1.0/di/struct.ScopedServiceProvider.html +[`create_scope`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceProvider.html#method.create_scope +[`get`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceProvider.html#method.get +[`get_mut`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceProvider.html#method.get_mut +[`get_by_key`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceProvider.html#method.get_by_key +[`get_by_key_mut`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceProvider.html#method.get_by_key_mut +[`get_all`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceProvider.html#method.get_all +[`get_all_mut`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceProvider.html#method.get_all_mut +[`get_all_by_key`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceProvider.html#method.get_all_by_key +[`get_all_by_key_mut`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceProvider.html#method.get_all_by_key_mut +[`get_required`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceProvider.html#method.get_required +[`get_required_mut`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceProvider.html#method.get_required_mut +[`get_required_by_key`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceProvider.html#method.get_required_by_key +[`get_required_by_key_mut`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceProvider.html#method.get_required_by_key_mut -[`Injectable`]: https://docs.rs/more-di/3.0.0/di/trait.Injectable.html -[`InjectBuilder`]: https://docs.rs/more-di/3.0.0/di/struct.InjectBuilder.html -[`as_mut`]: https://docs.rs/more-di/3.0.0/di/struct.InjectBuilder.html#method.as_mut -[`with_key`]: https://docs.rs/more-di/3.0.0/di/struct.InjectBuilder.html#method.with_key +[`Injectable`]: https://docs.rs/more-di/3.1.0/di/trait.Injectable.html +[`InjectBuilder`]: https://docs.rs/more-di/3.1.0/di/struct.InjectBuilder.html +[`as_mut`]: https://docs.rs/more-di/3.1.0/di/struct.InjectBuilder.html#method.as_mut +[`with_key`]: https://docs.rs/more-di/3.1.0/di/struct.InjectBuilder.html#method.with_key -[`ServiceDescriptorBuilder`]: https://docs.rs/more-di/3.0.0/di/struct.ServiceDescriptorBuilder.html -[`singleton`]: https://docs.rs/more-di/3.0.0/di/fn.singleton.html -[`singleton_as_self`]: https://docs.rs/more-di/3.0.0/di/fn.singleton_as_self.html -[`singleton_factory`]: https://docs.rs/more-di/3.0.0/di/fn.singleton_factory.html -[`singleton_with_key`]: https://docs.rs/more-di/3.0.0/di/fn.singleton_with_key.html -[`singleton_with_key_factory`]: https://docs.rs/more-di/3.0.0/di/fn.singleton_with_key_factory.html -[`scoped`]: https://docs.rs/more-di/3.0.0/di/fn.scoped.html -[`scoped_factory`]: https://docs.rs/more-di/3.0.0/di/fn.scoped_factory.html -[`scoped_with_key`]: https://docs.rs/more-di/3.0.0/di/fn.scoped_with_key.html -[`scoped_with_key_factory`]: https://docs.rs/more-di/3.0.0/di/fn.scoped_with_key_factory.html -[`transient`]: https://docs.rs/more-di/3.0.0/di/fn.transient.html -[`transient_factory`]: https://docs.rs/more-di/3.0.0/di/fn.transient_factory.html -[`transient_as_self`]: https://docs.rs/more-di/3.0.0/di/fn.transient_as_self.html -[`transient_with_key`]: https://docs.rs/more-di/3.0.0/di/fn.transient_with_key.html -[`transient_with_key_factory`]: https://docs.rs/more-di/3.0.0/di/fn.transient_with_key_factory.html -[`transient_with_key_as_self`]: https://docs.rs/more-di/3.0.0/di/fn.transient_with_key_as_self.html -[`existing`]: https://docs.rs/more-di/3.0.0/di/fn.existing.html -[`existing_as_self`]: https://docs.rs/more-di/3.0.0/di/fn.existing_as_self.html -[`existing_with_key`]: https://docs.rs/more-di/3.0.0/di/fn.existing_with_key.html -[`existing_with_key_as_self`]: https://docs.rs/more-di/3.0.0/di/fn.existing_with_key_as_self.html -[`exactly_one`]: https://docs.rs/more-di/3.0.0/di/fn.exactly_one.html -[`exactly_one_with_key`]: https://docs.rs/more-di/3.0.0/di/fn.exactly_one_with_key.html -[`zero_or_one`]: https://docs.rs/more-di/3.0.0/di/fn.zero_or_one.html -[`zero_or_one_with_key`]: https://docs.rs/more-di/3.0.0/di/fn.zero_or_one_with_key.html -[`zero_or_more`]: https://docs.rs/more-di/3.0.0/di/fn.zero_or_more.html -[`zero_or_more_with_key`]: https://docs.rs/more-di/3.0.0/di/fn.zero_or_more_with_key.html +[`ServiceDescriptorBuilder`]: https://docs.rs/more-di/3.1.0/di/struct.ServiceDescriptorBuilder.html +[`singleton`]: https://docs.rs/more-di/3.1.0/di/fn.singleton.html +[`singleton_as_self`]: https://docs.rs/more-di/3.1.0/di/fn.singleton_as_self.html +[`singleton_factory`]: https://docs.rs/more-di/3.1.0/di/fn.singleton_factory.html +[`singleton_with_key`]: https://docs.rs/more-di/3.1.0/di/fn.singleton_with_key.html +[`singleton_with_key_factory`]: https://docs.rs/more-di/3.1.0/di/fn.singleton_with_key_factory.html +[`scoped`]: https://docs.rs/more-di/3.1.0/di/fn.scoped.html +[`scoped_factory`]: https://docs.rs/more-di/3.1.0/di/fn.scoped_factory.html +[`scoped_with_key`]: https://docs.rs/more-di/3.1.0/di/fn.scoped_with_key.html +[`scoped_with_key_factory`]: https://docs.rs/more-di/3.1.0/di/fn.scoped_with_key_factory.html +[`transient`]: https://docs.rs/more-di/3.1.0/di/fn.transient.html +[`transient_factory`]: https://docs.rs/more-di/3.1.0/di/fn.transient_factory.html +[`transient_as_self`]: https://docs.rs/more-di/3.1.0/di/fn.transient_as_self.html +[`transient_with_key`]: https://docs.rs/more-di/3.1.0/di/fn.transient_with_key.html +[`transient_with_key_factory`]: https://docs.rs/more-di/3.1.0/di/fn.transient_with_key_factory.html +[`transient_with_key_as_self`]: https://docs.rs/more-di/3.1.0/di/fn.transient_with_key_as_self.html +[`existing`]: https://docs.rs/more-di/3.1.0/di/fn.existing.html +[`existing_as_self`]: https://docs.rs/more-di/3.1.0/di/fn.existing_as_self.html +[`existing_with_key`]: https://docs.rs/more-di/3.1.0/di/fn.existing_with_key.html +[`existing_with_key_as_self`]: https://docs.rs/more-di/3.1.0/di/fn.existing_with_key_as_self.html +[`exactly_one`]: https://docs.rs/more-di/3.1.0/di/fn.exactly_one.html +[`exactly_one_with_key`]: https://docs.rs/more-di/3.1.0/di/fn.exactly_one_with_key.html +[`zero_or_one`]: https://docs.rs/more-di/3.1.0/di/fn.zero_or_one.html +[`zero_or_one_with_key`]: https://docs.rs/more-di/3.1.0/di/fn.zero_or_one_with_key.html +[`zero_or_more`]: https://docs.rs/more-di/3.1.0/di/fn.zero_or_more.html +[`zero_or_more_with_key`]: https://docs.rs/more-di/3.1.0/di/fn.zero_or_more_with_key.html -[`lazy`]: https://docs.rs/more-di/3.0.0/di/lazy/index.html -[`Lazy`]: https://docs.rs/more-di/3.0.0/di/lazy/struct.Lazy.html -[`lazy::exactly_one`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.exactly_one.html -[`lazy::exactly_one_mut`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.exactly_one_mut.html -[`lazy::exactly_one_with_key`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.exactly_one_with_key.html -[`lazy::exactly_one_with_key_mut`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.exactly_one_with_key_mut.html -[`lazy::zero_or_one`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.zero_or_one.html -[`lazy::zero_or_one_mut`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.zero_or_one_mut.html -[`lazy::zero_or_one_by_key`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.zero_or_one_by_key.html -[`lazy::zero_or_one_by_key_mut`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.zero_or_one_by_key_mut.html -[`lazy::zero_or_more`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.zero_or_more.html -[`lazy::zero_or_more_mut`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.zero_or_more_mut.html -[`lazy::zero_or_more_by_key`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.zero_or_more_by_key.html -[`lazy::zero_or_more_by_key_mut`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.zero_or_more_by_key_mut.html -[`lazy::missing`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.missing.html -[`lazy::missing_with_key`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.missing_with_key.html -[`lazy::empty`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.empty.html -[`lazy::empty_with_key`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.empty_with_key.html -[`lazy::init`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.init.html -[`lazy::init_mut`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.init_mut.html -[`lazy::init_by_key`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.init_by_key.html -[`lazy::init_by_key_mut`]: https://docs.rs/more-di/3.0.0/di/lazy/fn.init_by_key_mut.html \ No newline at end of file +[`lazy`]: https://docs.rs/more-di/3.1.0/di/lazy/index.html +[`Lazy`]: https://docs.rs/more-di/3.1.0/di/lazy/struct.Lazy.html +[`lazy::exactly_one`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.exactly_one.html +[`lazy::exactly_one_mut`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.exactly_one_mut.html +[`lazy::exactly_one_with_key`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.exactly_one_with_key.html +[`lazy::exactly_one_with_key_mut`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.exactly_one_with_key_mut.html +[`lazy::zero_or_one`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.zero_or_one.html +[`lazy::zero_or_one_mut`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.zero_or_one_mut.html +[`lazy::zero_or_one_by_key`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.zero_or_one_by_key.html +[`lazy::zero_or_one_by_key_mut`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.zero_or_one_by_key_mut.html +[`lazy::zero_or_more`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.zero_or_more.html +[`lazy::zero_or_more_mut`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.zero_or_more_mut.html +[`lazy::zero_or_more_by_key`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.zero_or_more_by_key.html +[`lazy::zero_or_more_by_key_mut`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.zero_or_more_by_key_mut.html +[`lazy::missing`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.missing.html +[`lazy::missing_with_key`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.missing_with_key.html +[`lazy::empty`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.empty.html +[`lazy::empty_with_key`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.empty_with_key.html +[`lazy::init`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.init.html +[`lazy::init_mut`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.init_mut.html +[`lazy::init_by_key`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.init_by_key.html +[`lazy::init_by_key_mut`]: https://docs.rs/more-di/3.1.0/di/lazy/fn.init_by_key_mut.html \ No newline at end of file diff --git a/guide/src/guide/macros.md b/guide/src/guide/macros.md index 62c3aab..e3cb210 100644 --- a/guide/src/guide/macros.md +++ b/guide/src/guide/macros.md @@ -95,6 +95,30 @@ pub trait Foo; pub struct FooImpl; ``` +### Multiple Traits + +In most scenarios where you want to inject a trait, you will specify a single trait. There are a limited number of edge cases where you might need to specify multiple traits. The most common use case will be implementing a trait for a struct that is thread-safe, but the trait definition does not declare that itself. + +```rust +use di::*; + +pub trait Foo; + +#[injectable(Foo + Send + Sync)] // dyn Foo + Send + Sync → FooImpl +pub struct FooImpl; +``` + +Note that the combination of all traits now defines the service. The complete set of traits must be specified in order to resolve the service. + +```rust +let provider = ServiceCollection::new() + .add(FooImpl::transient()) + .build_provider() + .unwrap(); + +let foo = provider.get_required::(); +``` + ### Injection Rules The most basic form of injection allows `#[injectable]` to be applied to any struct or tuple struct, including generics. diff --git a/src/di/Cargo.toml b/src/di/Cargo.toml index 49170d1..829806c 100644 --- a/src/di/Cargo.toml +++ b/src/di/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "more-di" -version = "3.0.0" +version = "3.1.0" edition = "2018" authors = ["Chris Martinez "] description = "Provides support for dependency injection (DI)" @@ -32,7 +32,7 @@ alias = ["more-di-macros/alias"] [dependencies.more-di-macros] path = "../di_macros" -version = "3.0" +version = "3.1" optional = true [dependencies.spin] diff --git a/src/di/keyed.rs b/src/di/keyed.rs index c82b6aa..4b3c05f 100644 --- a/src/di/keyed.rs +++ b/src/di/keyed.rs @@ -2,7 +2,7 @@ use crate::{Mut, Ref}; use std::{any::Any, borrow::Borrow, marker::PhantomData, ops::Deref}; /// Represents a holder for a keyed service. -#[derive(Clone)] +#[derive(Debug)] pub struct KeyedRef { service: Ref, _key: PhantomData, @@ -23,6 +23,15 @@ impl KeyedRef { } } +impl Clone for KeyedRef { + fn clone(&self) -> Self { + Self { + service: self.service.clone(), + _key: PhantomData, + } + } +} + impl From> for Ref { fn from(value: KeyedRef) -> Self { value.service diff --git a/src/di/lib.rs b/src/di/lib.rs index 63a240b..36b004c 100644 --- a/src/di/lib.rs +++ b/src/di/lib.rs @@ -1,11 +1,19 @@ #![doc = include_str!("README.md")] #![cfg_attr(docsrs, feature(doc_cfg))] +// Mut is public primarily for code generation in the proc macro. it is +// generally uninteresting, but is required because, while we can detect a +// mutable service, we don't know which alias is behind the 'async' feature. +// the documentation will remain hidden to avoid confusion unless you really, +// really know and need to use it. + +#[doc(hidden)] #[cfg(not(feature = "async"))] -pub(crate) type Mut = std::cell::RefCell; +pub type Mut = std::cell::RefCell; +#[doc(hidden)] #[cfg(feature = "async")] -pub(crate) type Mut = std::sync::RwLock; +pub type Mut = std::sync::RwLock; mod collection; mod dependency; diff --git a/src/di_macros/Cargo.toml b/src/di_macros/Cargo.toml index 5f1f0a0..59f0874 100644 --- a/src/di_macros/Cargo.toml +++ b/src/di_macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "more-di-macros" -version = "3.0.0" +version = "3.1.0" edition = "2018" authors = ["Chris Martinez "] description = "Macro implementation of #[injectable(Trait)]" diff --git a/src/di_macros/internal/attribute.rs b/src/di_macros/internal/attribute.rs index e433332..caf69ba 100644 --- a/src/di_macros/internal/attribute.rs +++ b/src/di_macros/internal/attribute.rs @@ -1,16 +1,16 @@ use syn::{ parse::{Parse, ParseStream}, - Path, Result, + Path, Result, punctuated::Punctuated, token::Plus, }; pub struct InjectableAttribute { - pub trait_: Option, + pub trait_: Option>, } impl Parse for InjectableAttribute { fn parse(input: ParseStream) -> Result { Ok(Self { - trait_: input.parse().ok(), + trait_: input.parse_terminated(Path::parse, Plus).ok(), }) } } diff --git a/src/di_macros/internal/derive.rs b/src/di_macros/internal/derive.rs index 15cddb5..f59631d 100644 --- a/src/di_macros/internal/derive.rs +++ b/src/di_macros/internal/derive.rs @@ -1,4 +1,4 @@ -use syn::{Generics, ItemStruct, Path, Signature}; +use syn::{punctuated::Punctuated, token::Plus, Generics, ItemStruct, Path, Signature}; pub enum MacroTarget<'a> { Method(&'a Signature), @@ -8,7 +8,7 @@ pub enum MacroTarget<'a> { pub struct DeriveContext<'a> { pub generics: &'a Generics, pub implementation: &'a Path, - pub service: &'a Path, + pub service: Punctuated, target: MacroTarget<'a>, } @@ -16,7 +16,7 @@ impl<'a> DeriveContext<'a> { pub fn for_method( generics: &'a Generics, implementation: &'a Path, - service: &'a Path, + service: Punctuated, method: &'a Signature, ) -> Self { Self { @@ -30,7 +30,7 @@ impl<'a> DeriveContext<'a> { pub fn for_struct( generics: &'a Generics, implementation: &'a Path, - service: &'a Path, + service: Punctuated, struct_: &'a ItemStruct, ) -> Self { Self { @@ -46,8 +46,12 @@ impl<'a> DeriveContext<'a> { } pub fn is_trait(&self) -> bool { + if self.service.len() > 1 { + return true; + } + let impl_ = &self.implementation.segments.last().unwrap().ident; - let struct_ = &self.service.segments.last().unwrap().ident; + let struct_ = &self.service.first().unwrap().segments.last().unwrap().ident; impl_ != struct_ } } diff --git a/src/di_macros/internal/derive_trait.rs b/src/di_macros/internal/derive_trait.rs index b685fc8..a2201e2 100644 --- a/src/di_macros/internal/derive_trait.rs +++ b/src/di_macros/internal/derive_trait.rs @@ -23,8 +23,8 @@ impl InjectableTrait { } let service = if context.is_trait() { - let svc = context.service; - quote! { dyn #svc } + let svc = context.service.iter(); + quote! { dyn #(#svc)+* } } else { quote! { Self } }; @@ -48,7 +48,7 @@ impl InjectableTrait { }; let activate2 = activate.clone(); let code = quote! { - impl#generics di::Injectable for #implementation #where_ { + impl #generics di::Injectable for #implementation #where_ { fn inject(lifetime: di::ServiceLifetime) -> di::InjectBuilder { di::InjectBuilder::new( di::Activator::new::<#service, Self>( diff --git a/src/di_macros/internal/injector.rs b/src/di_macros/internal/injector.rs index 5dab41c..be6728f 100644 --- a/src/di_macros/internal/injector.rs +++ b/src/di_macros/internal/injector.rs @@ -25,9 +25,17 @@ pub trait CallSiteInjector<'a> { } else { quote! { sp.get_by_key::<#key, #svc>() } }, - dependency: Some( - quote! { di::ServiceDependency::new(di::Type::keyed::<#key, #svc>(), di::ServiceCardinality::ZeroOrOne) }, - ), + dependency: if context.mutable { + Some(quote! { + di::ServiceDependency::new( + di::Type::keyed::<#key, di::Mut<#svc>>(), + di::ServiceCardinality::ZeroOrOne) }) + } else { + Some(quote! { + di::ServiceDependency::new( + di::Type::keyed::<#key, #svc>(), + di::ServiceCardinality::ZeroOrOne) }) + }, } } else { InjectedCallSite { @@ -42,9 +50,17 @@ pub trait CallSiteInjector<'a> { } else { quote! { sp.get::<#svc>() } }, - dependency: Some( - quote! { di::ServiceDependency::new(di::Type::of::<#svc>(), di::ServiceCardinality::ZeroOrOne) }, - ), + dependency: if context.mutable { + Some(quote! { + di::ServiceDependency::new( + di::Type::of::>(), + di::ServiceCardinality::ZeroOrOne) }) + } else { + Some(quote! { + di::ServiceDependency::new( + di::Type::of::<#svc>(), + di::ServiceCardinality::ZeroOrOne) }) + }, } } } @@ -65,9 +81,17 @@ pub trait CallSiteInjector<'a> { } else { quote! { sp.get_required_by_key::<#key, #svc>() } }, - dependency: Some( - quote! { di::ServiceDependency::new(di::Type::keyed::<#key, #svc>(), di::ServiceCardinality::ExactlyOne) }, - ), + dependency: if context.mutable { + Some(quote! { + di::ServiceDependency::new( + di::Type::keyed::<#key, di::Mut<#svc>>(), + di::ServiceCardinality::ExactlyOne) }) + } else { + Some(quote! { + di::ServiceDependency::new( + di::Type::keyed::<#key, #svc>(), + di::ServiceCardinality::ExactlyOne) }) + }, } } else { InjectedCallSite { @@ -82,9 +106,17 @@ pub trait CallSiteInjector<'a> { } else { quote! { sp.get_required::<#svc>() } }, - dependency: Some( - quote! { di::ServiceDependency::new(di::Type::of::<#svc>(), di::ServiceCardinality::ExactlyOne) }, - ), + dependency: if context.mutable { + Some(quote! { + di::ServiceDependency::new( + di::Type::of::>(), + di::ServiceCardinality::ExactlyOne) }) + } else { + Some(quote! { + di::ServiceDependency::new( + di::Type::of::<#svc>(), + di::ServiceCardinality::ExactlyOne) }) + }, } } } @@ -113,9 +145,17 @@ pub trait CallSiteInjector<'a> { quote! { sp.get_all_by_key::<#key, #svc>().collect() } } }, - dependency: Some( - quote! { di::ServiceDependency::new(di::Type::keyed::<#key, #svc>(), di::ServiceCardinality::ZeroOrMore) }, - ), + dependency: if context.mutable { + Some(quote! { + di::ServiceDependency::new( + di::Type::keyed::<#key, di::Mut<#svc>>(), + di::ServiceCardinality::ZeroOrMore) }) + } else { + Some(quote! { + di::ServiceDependency::new( + di::Type::keyed::<#key, #svc>(), + di::ServiceCardinality::ZeroOrMore) }) + }, } } else { InjectedCallSite { @@ -138,9 +178,17 @@ pub trait CallSiteInjector<'a> { quote! { sp.get_all::<#svc>().collect() } } }, - dependency: Some( - quote! { di::ServiceDependency::new(di::Type::of::<#svc>(), di::ServiceCardinality::ZeroOrMore) }, - ), + dependency: if context.mutable { + Some(quote! { + di::ServiceDependency::new( + di::Type::of::>(), + di::ServiceCardinality::ZeroOrMore) }) + } else { + Some(quote! { + di::ServiceDependency::new( + di::Type::of::<#svc>(), + di::ServiceCardinality::ZeroOrMore) }) + }, } } } diff --git a/src/di_macros/lib.rs b/src/di_macros/lib.rs index 2694f89..9a872df 100644 --- a/src/di_macros/lib.rs +++ b/src/di_macros/lib.rs @@ -6,7 +6,12 @@ extern crate proc_macro; use crate::internal::*; use internal::{Constructor, DeriveContext, InjectableTrait}; use proc_macro2::TokenStream; -use syn::{punctuated::Punctuated, spanned::Spanned, token::PathSep, *}; +use syn::{ + punctuated::Punctuated, + spanned::Spanned, + token::{PathSep, Plus}, + *, +}; /// Represents the metadata used to identify an injected function. /// @@ -164,11 +169,10 @@ fn derive_from_struct_impl( ) -> Result { if let Type::Path(type_) = &*impl_.self_ty { let imp = &type_.path; - let svc = attribute.trait_.as_ref().unwrap_or(imp); - + let svc = service_from_attribute(imp, attribute); match Constructor::select(&impl_, imp) { Ok(method) => { - let context = DeriveContext::for_method(&impl_.generics, imp, &svc, method); + let context = DeriveContext::for_method(&impl_.generics, imp, svc, method); derive(context, original) } Err(error) => Err(error), @@ -184,12 +188,22 @@ fn derive_from_struct( original: TokenStream, ) -> Result { let imp = &build_path_from_struct(&struct_); - let svc = attribute.trait_.as_ref().unwrap_or(imp); + let svc = service_from_attribute(imp, attribute); let context = DeriveContext::for_struct(&struct_.generics, imp, svc, &struct_); derive(context, original) } +fn service_from_attribute(impl_: &Path, mut attribute: InjectableAttribute) -> Punctuated { + let mut punctuated = attribute.trait_.take().unwrap_or_else(Punctuated::::new); + + if punctuated.is_empty() { + punctuated.push(impl_.clone()); + } + + punctuated +} + fn build_path_from_struct(struct_: &ItemStruct) -> Path { let generics = &struct_.generics; let mut segments = Punctuated::::new(); @@ -279,7 +293,8 @@ mod test { "| sp : & di :: ServiceProvider | di :: RefMut :: new (Self :: new () . into ())) , ", "lifetime) ", "} ", - "}"); + "}" + ); assert_eq!(expected, result.to_string()); } @@ -516,7 +531,8 @@ mod test { "| sp : & di :: ServiceProvider | di :: RefMut :: new (Self :: new () . into ())) , ", "lifetime) ", "} ", - "}"); + "}" + ); assert_eq!(expected, result.to_string()); } @@ -593,7 +609,8 @@ mod test { "| sp : & di :: ServiceProvider | di :: RefMut :: new (Self :: new () . into ())) , ", "lifetime) ", "} ", - "}"); + "}" + ); assert_eq!(expected, result.to_string()); } diff --git a/test/di/scenarios.rs b/test/di/scenarios.rs index 75fd209..9025f77 100644 --- a/test/di/scenarios.rs +++ b/test/di/scenarios.rs @@ -408,6 +408,22 @@ fn inject_should_resolve_mut() { // no panic! } +#[test] +fn inject_should_resolve_mut_dependency() { + // arrange + let provider = ServiceCollection::new() + .add(mutable::MutDep::transient().as_mut()) + .add(mutable::MutStruct::transient()) + .build_provider() + .unwrap(); + + // act + let _ = provider.get_required::(); + + // assert + // no panic! +} + #[test] fn inject_should_resolve_keyed_mut() { // arrange @@ -428,7 +444,7 @@ fn inject_should_resolve_keyed_mut() { } #[test] -fn inject_should_support_multiple_traits() { +fn inject_should_resolve_multiple_traits() { // arrange let provider = ServiceCollection::new() .add(MultiService::singleton()) @@ -448,3 +464,25 @@ fn inject_should_support_multiple_traits() { // assert // no panic! } + +#[test] +fn inject_should_support_multiple_traits() { + // arrange + trait IPityTheFoo {} + + #[injectable(IPityTheFoo + Send + Sync)] + struct Foo; + + impl IPityTheFoo for Foo {} + + let provider = ServiceCollection::new() + .add(Foo::transient()) + .build_provider() + .unwrap(); + + // act + let _ = provider.get_required::(); + + // assert + // no panic! +}