Closed pgokul closed 3 years ago
The Component
derive automatically generates a parameters struct (ex. IServiceImplParameters
) which can be used to supply all of the non-injected properties during module creation. Here's the section of the documentation which talks about this: https://docs.rs/shaku/0.5.0/shaku/guide/index.html#passing-parameters
If the type of a property implements Default
, you don't need to manually pass it in to get the default value for the type. For example, your IServiceImpl
seems to only need a default value for RwLock<HashMap<String, String>>
, so you don't need to use with_component_parameters
on it.
In your example, it looks like you also need to declare a module to hold your components. Your code might end up looking like this:
module! {
MyModule {
components = [IConfigImpl, IServiceImpl],
providers = []
}
}
fn main() {
let my_mod = MyModule::builder()
.with_component_parameters::<IConfigImpl>(IConfigImplParameters {
url: "insert-url-here".to_string()
})
.build();
}
Note: In Rust, traits and structs are normally named without a leading "I". For example, your IService
and IServiceImpl
would be Service
and ServiceImpl
.
Thanks @Mcat12 . Would this mean that the code which creates the modules needs to be aware of the internal details of the component to be created if the component has some properties which require non trivial construction. This would mean the internal details of the object would be leaking into the module creators. Is support planned/available for something like @PostConstruct annotation in java JSR 250.
You can also add an annotation to the property to provide a default, which would move that code closer to the implementation:
#[derive(Component)]
#[shaku(interface = IConfig)]
struct IConfigImpl {
#[shaku(default = "insert-url-here".to_string())]
url: String,
}
The default
field in the annotation is an expression, so it can be a function call to more complex code.
Alternatively, you could write the Component
implementation by hand to have full control over how the service gets created:
struct IServiceImpl {
config: Arc<dyn IConfig>,
usr_store: RwLock<HashMap<String, String>>,
}
impl<M: Module + HasComponent<dyn IConfig>> Component<M> for IServiceImpl {
type Interface = dyn IService;
type Parameters = ();
fn build(context: &mut ModuleBuildContext<M>, params: Self::Parameters) -> Box<Self::Interface> {
// Insert initialization code here
let usr_store = RwLock::default();
Box::new(IServiceImpl {
config: M::build_component(context),
usr_store
})
}
}
Note that the Component
derive and shaku
annotations are removed, since the impl is written manually. Manually writing the Component
impl isn't too complicated, so I think it removes the need for a special @PostConstruct
-like function.
Writing the Component implementation by hand seems to be the cleanest way to do this. I will try this.
Thanks for the question!
I am new to Rust and and am unable to figure out how to use a constructor with shaku dependency injection.
I am getting an error, ' missing field
config
in initializer of IServiceImplAdding default throws an error "the trait
std::default::Default
is not implemented fordyn IConfig::IConfig
"How can i use a constructor where certain fields of the struct are initialized and certain others are injected.