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)) } } }