3 releases
Uses old Rust 2015
0.1.1-rc.2 | Jun 29, 2023 |
---|---|
0.1.0 | Jun 28, 2023 |
#39 in #atom
12KB
125 lines
Natom
Natom is a minimal CDI implementation in rust.
lib.rs
:
This crate is a minimal CDI implementation for rust. While the CDI implementation is not standard conformant in any way, it has some resemblence to WELD.
CDI introduction
CDI is a broad topic, and not easily covered.
Atoms
Atoms (or in WELD, beans) are the cornerstone of the CDI system. An atom is simply a managed struct; managed meaning that construction and destruction is handled by the context. In a big application, you may have multiple application-wide objects you want to pass around and inject (which in reality just means use in someone's constructor). This can become very tedious. Atoms are suitable for both (singleton) application-wide objects, but also creating new instances.
Atoms are created by annotating a struct with [macro@atom
].
#[atom]
struct Foo {}
Context
Atoms are created inside a context.
Scopes
Every atom has a specified scope. Scope refers to how the atom is constructed and reused. The behaviour of injections depends on the scope of the atom. The following scopes are implemented:
- Entrypoint:
- Singleton:
- Dependent:
Relations & Inject
When building a service-oriented system, one might split the logic into many different objects, each having some relation to the other but also being a logical unit in-and-off itself. As such, we need a way to represent relations.
#[atom]
struct FooService {
#[inject]
repository: FooRepository,
}
#[atom]
struct FooRepository {}
We do this by using the [macro@inject
] macro. We simply inject an instance into
our own constructor. The instance could be shared between different FooService
instances, or unique, depending on the scope of FooRepository
.
Produces
Sometimes, we want to inject objects which are not atoms. This could be third-party dependencies, but also random numbers.
We also want to be able to name the alternative of the producer.
#[atom]
struct IntProducer {
}
impl IntProducer {
#[produces]
fn make_int() i32 {
0
}
}
#[atom]
struct Foo {
#[inject]
number: i32,
}
Lifetime management
Sometimes, it's not enough to inject all dependencies at construction, or just remove
the object at destruction. One might need to close files, sockets, or any other persistance.
For this, there is functionality to specify behaviour inside custom functions annotated
[macro@construct
], [macro@destruct
].
Builtin atoms
While the core CDI implementation is useful, there are some parts relevant to the whole ecosystem.
Configuration
For configuration, we use something called configuration properties.
Fields annotated with [macro@config_property
] are automatically marked with
[macro@inject
]. [macro@config_property
] takes a name of a configuration property
when injecting, and looks up that property at runtime. The property can be
defaulted and loaded from ENV.
#[atom]
struct Foo {
#[config_property(foo.speed)]
speed: f32,
}
The types that [macro@config_property
] accepts are:
- [String]
- Integers ([
i8
], [i16
], [u8
], [u16
], ...) - Floats ([
f32
], [f64
])
Scheduling
For scheduling tasks ...
Example application
#[atom]
struct Foo {
}
#[atom]
struct Bar {
}
define_base_atoms!();
fn main() {
let context = Context::new(BaseAtomRegistry {});
context.start();
}
Dependencies
~230–670KB
~16K SLoC