abscissa_core/
component.rs

1//! Application components: extensions/plugins for Abscissa applications.
2//!
3//! See docs on the `Component` trait for more information.
4
5#![allow(unused_variables)]
6
7mod handle;
8mod id;
9pub mod registry;
10
11pub use self::{handle::Handle, id::Id, registry::Registry};
12pub use abscissa_derive::{Component, Injectable};
13
14use crate::{FrameworkError, Version, application::Application, shutdown::Shutdown};
15use std::{any::Any, cmp::Ordering, fmt::Debug, slice::Iter};
16
17/// The aspect of application components associated with dependency injection.
18///
19/// This exists as a separate trait to enable separately deriving this trait and
20/// manually implementing [`Component`]. See the [`Component`] documentation for
21/// full details.
22pub trait Injectable<A>: AsAny + Debug + Send + Sync
23where
24    A: Application,
25{
26    /// Identifier for this component.
27    ///
28    /// These are the Rust path (e.g. `crate_name:foo::Foo`) by convention.
29    fn id(&self) -> Id;
30
31    /// Version of this component
32    fn version(&self) -> Version;
33
34    /// Names of the components this component depends on.
35    ///
36    /// After this app's `after_config` callback is fired, the
37    /// `register_dependency` callback below will be fired for each of these.
38    fn dependencies(&self) -> Iter<'_, Id> {
39        [].iter()
40    }
41
42    /// Register a dependency of this component (a.k.a. "dependency injection")
43    fn register_dependency(
44        &mut self,
45        handle: Handle,
46        dependency: &mut dyn Component<A>,
47    ) -> Result<(), FrameworkError> {
48        unimplemented!();
49    }
50}
51
52/// Application components.
53///
54/// Components are Abscissa's primary extension mechanism, and are aware of
55/// the application lifecycle. They are owned by the application as boxed trait
56/// objects in a runtime type registry which is aware of a dependency ordering
57/// and can (potentially in the future) support runtime reinitialization.
58///
59/// During application initialization, callbacks are sent to all components
60/// upon events like application configuration being loaded. The
61/// `register_dependency` callback is called for each dependency returned
62/// by the `dependencies` method.
63///
64/// Additionally, they receive a callback prior to application shutdown.
65///
66/// ## Custom Derive
67///
68/// The main intended way to impl this trait is by using the built-in custom
69/// derive functionality.
70///
71/// ```rust
72/// use abscissa_core::Component;
73///
74/// #[derive(Component, Debug)]
75/// pub struct MyComponent {}
76/// ```
77///
78/// This will automatically implement the entire trait for you.
79///
80/// If you want your component to react to lifecycle events, you can instead
81/// just derive the dependency injection functionality, and implement this trait
82/// manually.
83///
84/// ```no_compile
85/// use abscissa_core::{Component, Injectable};
86///
87/// #[derive(Injectable, Debug)]
88/// pub struct MyComponent {}
89///
90/// impl Component<MyApplication> for MyComponent {
91///     fn after_config(&mut self, config: &MyConfig) -> Result<(), FrameworkError> {
92///         // Do something with the config
93///         Ok(())
94///     }
95/// }
96/// ```
97pub trait Component<A>: Injectable<A>
98where
99    A: Application,
100{
101    /// Lifecycle event called when application configuration should be loaded
102    /// if it were possible.
103    fn after_config(&mut self, config: &A::Cfg) -> Result<(), FrameworkError> {
104        Ok(())
105    }
106
107    /// Perform any tasks which should occur before the app exits
108    fn before_shutdown(&self, kind: Shutdown) -> Result<(), FrameworkError> {
109        Ok(())
110    }
111}
112
113impl<A> PartialEq for Box<dyn Component<A>>
114where
115    A: Application,
116{
117    fn eq(&self, other: &Self) -> bool {
118        self.id() == other.id()
119    }
120}
121
122impl<A> PartialOrd for Box<dyn Component<A>>
123where
124    A: Application,
125{
126    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
127        if other.dependencies().any(|dep| *dep == self.id()) {
128            if self.dependencies().any(|dep| *dep == other.id()) {
129                None
130            } else {
131                Some(Ordering::Greater)
132            }
133        } else if self.dependencies().any(|dep| *dep == other.id()) {
134            Some(Ordering::Less)
135        } else {
136            Some(Ordering::Equal)
137        }
138    }
139}
140
141/// Dynamic type helper trait
142// TODO(tarcieri): eliminate this trait or hide it from the public API
143pub trait AsAny: Any {
144    /// Borrow this concrete type as a `&dyn Any`
145    fn as_any(&self) -> &dyn Any;
146
147    /// Borrow this concrete type as a `&mut dyn Any`
148    fn as_mut_any(&mut self) -> &mut dyn Any;
149}
150
151impl<T> AsAny for T
152where
153    T: Any,
154{
155    fn as_any(&self) -> &dyn Any {
156        self
157    }
158
159    fn as_mut_any(&mut self) -> &mut dyn Any {
160        self
161    }
162}