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}