[][src]Crate coi_actix_web

This crate provides a simple Dependency Injection framework for actix-web.

Example

Note that the following example is heavily minified. Files names don't really matter. For a more involved example, please see the coi-actix-sample repository.

In your main binary:

This example is not tested
use crate::infrastructure::{RepositoryProvider, PoolProvider};
use crate::service::ServiceProvider;
use coi::container;
use actix_web::{App, HttpServer};

mod traits;
mod infrastructure;
mod routes;
mod service;

fn main() {
    // container! only expects identifiers, so construct this provider outside
    let postgres_pool = PoolProvider::<NoTls>::new(/* construct actual pool */);

    // Build your container
    let container = container! {
        pool => postgres_pool.singleton,
        service => ServiceProvider.scoped,
        repository => RepositoryProvider.scoped,
    };

    HttpServer::new(move || {
        App::new()
             // Make sure to assign it to `app_data` and not `data`
            .app_data(container.clone())
            .configure(routes::data::route_config)
    })
    ...
}

traits.rs:

This example is not tested
use coi::Inject;

// Ensure all of your traits inherit from `Inject`
pub trait IService: Inject {
    ...
}

pub trait IRepository: Inject {
    ...
}

service.rs

This example is not tested
use crate::traits::IService;
use coi::Inject;
use std::sync::Arc;

// derive `Inject` for all structs that will provide the injectable traits.
#[derive(Inject)]
#[coi(provides pub dyn IService with Service::new(repository))]
struct Service {
    #[coi(inject)]
    repository: Arc<dyn IRepository>,
}

impl IService for Service {
    ...
}

Note: See coi::Inject for more examples on how to use #[derive(Inject)]

infrastructure.rs

This example is not tested
use crate::traits::IRepository;
use coi::Inject;
use ...::PostgresPool;
#[cfg(feature = "notls")]
use ...::NoTls;
#[cfg(not(feature = "notls"))]
use ...::Tls;

#[derive(Inject)]
#[coi(provides pub dyn IRepository with Repository::new(pool))]
struct Repository {
    #[cfg(feature = "notls")]
    #[coi(inject)]
    pool: PostgresPool<NoTls>,

    #[cfg(not(feature = "notls"))]
    #[coi(inject)]
    pool: PostgresPool<Tls>,
}

impl IRepository for Repository {
    ...
}

#[derive(Inject)]
struct Pool<T> where T: ... {
    pool: PostgresPool<T>
}

#[derive(Provide)]
#[coi(provides pub Pool<T> with Pool::new(self.0.pool))]
struct PoolProvider<T> where T: ... {
    pool: PostgresPool<T>
}

impl<T> PoolProvider<T> where T: ... {
    fn new(PostgresPool<T>) -> Self { ... }
}

routes.rs

This example is not tested
use crate::service::IService;
use actix_web::{
    web::{self, HttpResponse, ServiceConfig},
    Responder,
};
use coi_actix_web::inject;
use std::sync::Arc;

#[inject(coi_crate = "coi")]
async fn get(
    id: web::Path<i64>,
    #[inject] service: Arc<dyn IService>,
) -> Result<impl Responder, ()> {
    let name: String = service.get(*id).await.map_err(|e| log::error!("{}", e))?;
    Ok(HttpResponse::Ok().json(name))
}

#[inject(coi_crate = "coi")]
async fn get_all(#[inject] service: Arc<dyn IService>) -> Result<impl Responder, ()> {
    let data: Vec<String> = service.get_all().await.map_err(|e| log::error!("{}", e))?;
    Ok(HttpResponse::Ok().json(data))
}

pub fn route_config(config: &mut ServiceConfig) {
    config.service(
        web::scope("/data")
            .route("", web::get().to(get_all))
            .route("/", web::get().to(get_all))
            .route("/{id}", web::get().to(get)),
    );
}

Traits

AppExt

Extensions to actix-web's App struct

Attribute Macros

inject

The #inject proc macro should only be applied to functions that will be passed to actix-web's routing APIs.