Crates.io | dose |
lib.rs | dose |
version | 0.1.3 |
source | src |
created_at | 2022-06-09 18:06:20.288556 |
updated_at | 2022-06-14 17:35:27.379412 |
description | Your daily dose of structs and functions. |
homepage | |
repository | https://github.com/noostech/dose |
max_upload_size | |
id | 602793 |
size | 49,017 |
Your daily dose of structs and functions.
Unfortunately we do not support runtime dependency cycle detection ... yet.
You need to initialize the crate with the init!
macro at the root of your project in lib.rs
.
This is a current limitation of rust and this may become unnecessary in future versions.
dose::init!();
You can register providers with the provide
attribute.
#[provide]
fn provide_something(context: &mut Context<Config>) -> Something {
Something::new(...)
}
The &mut Context<Config>
is always passed as argument and can be used to resolve other types.
The config is also available from the context context.config
.
Each time the type Something
is get!
, the function above will be called.
If you want the same instance to be used, the attribute parameter singleton
needs to be set to true
.
#[provide(singleton=true)]
fn provide_something(context: &mut Context<Config>) -> Something {
Something::new(...)
}
In this case, each time the type Something
is get!
, the instance will be cloned.
This really becomes a singleton if type Something
is wrapped in an std::sync::Arc
or a std::rc::Rc
.
#[provide(singleton=true)]
fn provide_something(context: &mut Context<Config>) -> Arc<Something> {
Arc::new(Something::new(...))
}
The get!
macro is the way to get the instance of a type.
let mut context = dose::Context::new(config);
let my_type: MyType = dose::get!(context);
This macro can also be used inside providers.
struct MyType {
a: TypeA,
b: TypeB,
}
#[provide]
fn provide_my_type(context: &mut Context<Config>) -> MyType {
MyType {
a: get!(context), // Type infered to TypeA
b: get!(context), // Type infered to TypeB
}
}
Note that if the provider of TypeA
or TypeB
is not registered, a compilation error will occur.
Providers should be defined in modules specifically created for declaring how each type is instantiated.
The Context<Config>
should be deleted (drop
) before the application is started.
Using out of scope.
use dose::{Context, get}
... // declare all modules
fn create_server(config: Config) -> Server {
let mut context = Context::new(config);
get!(context)
}
#[tokio::main]
async fn start_server_implicit() {
let server = create_server(Config::load(...));
// Implicitly drop the context because it becomes out of scope
server.start().await
}
#[tokio::main]
async fn start_server_explicit() {
let mut context = Context::new(config);
let server: Server = get!(context);
// Explicitly drop the context using std::mem::drop
std::mem::drop(context);
server.start().await
}
Keep providers simple, only creation logic should be in there. No algorithms or domain logic should be executed.
The config provided in the context can be anything, but a simple struct without functions and public fields can be used. Each provider should only require the context and the config to execute properly.