Crate rscontainer[][src]

Expand description

Manager for dependencies between objects for the Rust language.

How to use

The main type of this crate is the ServiceContainer. There are multiple ways to initialize the container. See the documentation on ServiceContainer for more information. The easiest way is with new().

use rscontainer::ServiceContainer;
let mut container = ServiceContainer::new();

To configure the container, such as overriding the default constructors, use the ContainerBuilder.

use rscontainer::ServiceContainer;
let mut container = ServiceContainer::builder()
    .with_owned_constructor::<MyService>(|_resolver, value| {
        Ok(MyService(value))
    })
    .build();

Resolving instances

There are different kind of instances:

  • Owned instances: a fresh instance to be used in an owned scope. This instance will not be stored in the service container, you will get a new instance each time you resolve an owned instance. See Resolver::owned().
  • Shared instances: an instance behind a smart pointer that is stored in the service container. You will get the same instance each time you resolve a shared service. See Resolver::shared() and Shared<T>.
  • Some instances: an enum over owned and shared instances. Use this in a type when you want the user of your type to decide what kind of instance they want to supply. See Instance, Resolver::shared_instance() and Resolver::owned_instance().

To resolve instances, you first need to acquire a Resolver.

let mut resolver = container.resolver();

To get an instance, you use one of the resolver methods. To resolve an owned instance, use the Resolver::owned() method. An owned service can define parameters that need to be supplied to the owned() method.

let mut owned_service = resolver.owned::<MyService>(120)?;

To resolve a shared instance, use the Resolver::shared() method. The first time that this service is resolved, it will be contructed, stored in the container and the pointer is returned. Every other time the service is resolved that same pointer will be cloned and returned. Therefore it is not possible to supply parameters.

let shared_service = resolver.shared::<MyService>()?;

Working with instances

An owned instance is just a normal, owned instance, therefore you can do with it whatever you want. But a shared instance is always behind a smart pointer and a locking or borrowing mechanism. To use the instance, you need to use one of the access methods: Shared::access(), Shared::access_mut(), Shared::try_access() and Shared::try_access_mut(), which borrow or lock the instance for the lifetime of the supplied closure. These access methods take into account that the service may be poisoned. See Poisoning for more information.

let value = shared_service.access(|service| {
    let service = service.assert_healthy();
    service.get_value()
});

Using a type as a service

To be able to resolve a type through the service container, there needs to be an implementation of IShared and/or IOwned for it. These traits define a constructor method. For an owned service it will be called each time it is resolved. For a shared service it will only be called the first time.

The constructors do not return Self, but rather an associated type defined on the traits. This makes it possible to resolve every type through the container without having to create a newtype wrapper.

The constructors also receive a Resolver, which can be used to recursively construct dependencies of the service. This is rscontainer’s implementation of dependency injection.

Example

use std::time::Instant;
use std::rc::Rc;
use std::cell::RefCell;
use rscontainer::{IShared, Resolver, ServiceContainer};

struct InstantService;
impl IShared for InstantService {
    type Pointer = Rc<RefCell<Instant>>;
    type Target = Instant;
    type Error = ();

    fn construct(_: Resolver) -> Result<Self::Pointer, Self::Error> {
        Ok(Rc::new(RefCell::new(Instant::now())))
    }
}

fn main() {
    let mut container = ServiceContainer::new();
    let instant = container.resolver().shared::<InstantService>().unwrap();
    instant.access(|instant| {
        let instant = instant.assert_healthy();
        println!("{:?}", instant);
    });
}

Modules

internals

Types for extending the functionality of rscontainer.

Structs

Access

Wrapper to make a type accessable through the IAccess trait.

ContainerBuilder

Create a container with the builder pattern.

Resolver

Used to resolve services from the service container.

ServiceContainer

Container for all the services of an application.

Shared

A pointer to a shared instance from the service container.

Enums

Instance

Some instance of a service, either a shared instance or owned instance.

Poisoning

Indicates whether an instance is poisoned or not.

Traits

IOwned

A type that can be used as an owned service.

IShared

A type that can be used as a shared service.