1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! # Sai
//!
//! Sai is a framework for managing lifecycle and dependency of your software components.
//! In some languages, it was called "IoC" and "Dependency Injection".
//! The main usecase of this framework is on medium/large scale web services.
//!
//! The Sai ecosystem consists of two major concepts: [System](struct.System.html), [Component](trait.Component.html).
//! A System is a runtime unit that control lifecycles of all Components.
//! A Component is a group of logic. A Component can depends on other Components and it can
//! also have its own internal state.

use std::boxed::Box;
use std::any::{TypeId};

/// Re-export from async_trait library
pub use async_trait::async_trait;

pub use sai_component_derive::Component;

mod injected;
pub use injected::Injected;

mod component_repository;
#[doc(hidden)]
pub use component_repository::ComponentRepository;

mod system;
pub use system::System;

mod downcast;

mod registry;
#[doc(inline)]
pub use registry::ComponentRegistry;

/// ComponentLifecycle is simply start()/stop()
///
/// You will have to manually implement this trait for your component if you want to have explict
/// startup/shutdown logic.
///
/// Check out the doc for [Component](trait.Component.html) trait
#[async_trait()]
pub trait ComponentLifecycle: Send { // Extend Send compiler stop complaining trait object issue
    async fn start(&mut self) {}
    async fn stop(&mut self) {}
}

/// A Component is a bunch of business-logic behaviors + startup logic.
///
/// Normally, you won't need to manually implement this.
///
/// **Defining components with no explict startup / shutdown logic**
/// ```
/// use sai::{Component, Injected};
/// #[derive(Component)]
/// struct Bar {}
///
/// #[derive(Component)]
/// struct Foo {
///     #[injected]
///     bar: Injected<Bar>
/// }
/// ```
/// In above example, `Foo` and `Bar` are both component. Foo *depends on* Bar, the dependency is
/// defined via the `#[injected]` attributes.
///
/// Note: a dependency injected by Sai has to be wrapped by `Injected` struct.
///
/// **Component with explict startup / shutdown logic**
/// ```
/// use sai::{Component, ComponentLifecycle, async_trait};
/// #[derive(Component)]
/// #[lifecycle]
/// struct Foo {
///     internal: Option<u32>
/// }
///
/// #[async_trait]
/// impl ComponentLifecycle for Foo {
///     async fn start(&mut self) {
///         self.internal = Some(42);
///     }
///     async fn stop(&mut self) {
///         // Any shutdown logic
///     }
/// }
/// ```
#[async_trait()]
pub trait Component: Send + downcast::Downcast + ComponentLifecycle {
    fn build(registry: &ComponentRepository) -> Self
        where Self: Sized;

    fn meta() -> ComponentMeta<Box<Self>>
        where Self: Sized;
}

#[doc(hidden)]
pub struct ComponentMeta<T: ?Sized> {
    pub depends_on: Vec<TypeId>,
    pub type_id: TypeId,
    pub build: Box<dyn Fn(&ComponentRepository) -> T>
}

impl<T: Component + 'static> From<ComponentMeta<Box<T>>> for ComponentMeta<Box<dyn Component>> {

    fn from(m: ComponentMeta<Box<T>>) -> Self {
        ComponentMeta {
            depends_on: m.depends_on.clone(),
            type_id: m.type_id,
            build: Box::new(move |r: &ComponentRepository| (m.build)(r))
        }
    }
}