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
//! Application components.

#![allow(unused_variables)]

mod name;
mod registry;

pub use self::{name::Name, registry::Registry};
use crate::{application::Application, error::FrameworkError, shutdown::Shutdown, Version};
use std::{cmp::Ordering, fmt::Debug, slice::Iter};

/// Application components.
///
/// Components are Abscissa's primary extension mechanism, and are aware of
/// the application lifecycle. They are owned by the application as boxed trait
/// objects in a runtime type registry which is aware of a dependency ordering
/// and can (potentially in the future) support runtime reinitialization.
///
/// During application initialization, callbacks are sent to all components
/// upon events like application configuration being loaded.
///
/// Additionally, they receive a callback prior to application shutdown.
// TODO(tarcieri): downcast support for accessing components as concrete types?
pub trait Component<A>: Debug + Send + Sync
where
    A: Application,
{
    /// Name of this component
    fn name(&self) -> Name;

    /// Version of this component
    fn version(&self) -> Version;

    /// Names of the components this components depends on
    fn dependencies(&self) -> Iter<'_, Name> {
        [].iter()
    }

    /// Lifecycle event called when application configuration should be loaded
    /// if it were possible.
    fn after_config(&mut self, config: &A::Cfg) -> Result<(), FrameworkError> {
        Ok(())
    }

    /// Perform any tasks which should occur before the app exits
    fn before_shutdown(&self, kind: Shutdown) -> Result<(), FrameworkError> {
        Ok(())
    }
}

impl<A> PartialEq for Box<dyn Component<A>>
where
    A: Application,
{
    fn eq(&self, other: &Self) -> bool {
        self.name() == other.name()
    }
}

impl<A> PartialOrd for Box<dyn Component<A>>
where
    A: Application,
{
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        if other.dependencies().any(|dep| *dep == self.name()) {
            if self.dependencies().any(|dep| *dep == other.name()) {
                None
            } else {
                Some(Ordering::Greater)
            }
        } else if self.dependencies().any(|dep| *dep == other.name()) {
            Some(Ordering::Less)
        } else {
            Some(Ordering::Equal)
        }
    }
}