File: component.rs

package info (click to toggle)
rust-abscissa-core 0.8.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 420 kB
  • sloc: makefile: 4
file content (153 lines) | stat: -rw-r--r-- 5,101 bytes parent folder | download
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! Tests for Abscissa's component functionality
#![cfg(feature = "default")]
mod example_app;

use self::example_app::{ExampleApp, ExampleConfig};
use abscissa_core::{component, Component, FrameworkError, FrameworkErrorKind::ComponentError};

/// ID for `FoobarComponent` (example component #1)
const FOOBAR_COMPONENT_ID: component::Id = component::Id::new("component::FoobarComponent");

/// ID for `BazComponent` (example component #2)
const BAZ_COMPONENT_ID: component::Id = component::Id::new("component::BazComponent");

/// ID for `QuuxComponent` (example component #3)
const QUUX_COMPONENT_ID: component::Id = component::Id::new("component::QuuxComponent");

/// Example component #1
#[derive(Component, Debug, Default)]
pub struct FoobarComponent {
    /// Component state
    pub state: Option<String>,
}

impl FoobarComponent {
    /// Set the state string to a particular value
    pub fn set_state(&mut self, state_str: &str) {
        self.state = Some(state_str.to_owned());
    }
}

/// Example component #2
#[derive(Component, Debug, Default)]
pub struct BazComponent {}

/// Example component #3
#[derive(Component, Debug, Default)]
#[component(inject = "init_foobar(component::FoobarComponent)")]
#[component(inject = "init_baz(component::BazComponent)")]
pub struct QuuxComponent {
    /// State of Foo at the time we were initialized?
    pub foobar_state: Option<String>,

    /// Did we get a callback that `Baz` has been initialized?
    pub baz_initialized: bool,
}

impl QuuxComponent {
    /// Callback run after `FoobarComponent` has been initialized
    pub fn init_foobar(&mut self, foobar: &mut FoobarComponent) -> Result<(), FrameworkError> {
        self.foobar_state = foobar.state.clone();
        foobar.state = Some("hijacked!".to_owned());
        Ok(())
    }

    /// Callback run after `BazComponent` has been initialized
    pub fn init_baz(&mut self, _baz: &BazComponent) -> Result<(), FrameworkError> {
        self.baz_initialized = true;
        Ok(())
    }
}

fn init_components() -> Vec<Box<dyn Component<ExampleApp>>> {
    let mut foobar = FoobarComponent::default();
    foobar.set_state("original foobar state");

    let component1 = Box::new(foobar);
    let component2 = Box::<BazComponent>::default();
    let component3 = Box::<QuuxComponent>::default();

    let components: Vec<Box<dyn Component<ExampleApp>>> = vec![component1, component2, component3];

    // Ensure component IDs are as expected
    assert_eq!(components[0].id(), FOOBAR_COMPONENT_ID);
    assert_eq!(components[1].id(), BAZ_COMPONENT_ID);
    assert_eq!(components[2].id(), QUUX_COMPONENT_ID);

    components
}

#[test]
fn component_registration() {
    let mut registry = component::Registry::default();
    assert!(registry.is_empty());

    let components = init_components();
    registry.register(components).unwrap();
    assert_eq!(registry.len(), 3);

    // Fetch components and make sure they got registered correctly
    let foobar_comp = registry.get_by_id(FOOBAR_COMPONENT_ID).unwrap();
    assert_eq!(foobar_comp.id(), FOOBAR_COMPONENT_ID);

    let baz_comp = registry.get_by_id(BAZ_COMPONENT_ID).unwrap();
    assert_eq!(baz_comp.id(), BAZ_COMPONENT_ID);

    let quux_comp = registry.get_by_id(QUUX_COMPONENT_ID).unwrap();
    assert_eq!(quux_comp.id(), QUUX_COMPONENT_ID);
}

#[test]
fn duplicate_component_registration() {
    let foobar1 = Box::<FoobarComponent>::default();
    let foobar2 = Box::<FoobarComponent>::default();
    let components: Vec<Box<dyn Component<ExampleApp>>> = vec![foobar1, foobar2];

    let mut registry = component::Registry::default();
    assert!(registry.is_empty());

    let err = registry.register(components).err().unwrap();
    assert_eq!(*err.kind(), ComponentError);
    assert_eq!(registry.len(), 1);

    let foobar = registry.get_by_id(FOOBAR_COMPONENT_ID).unwrap();
    assert_eq!(foobar.id(), FOOBAR_COMPONENT_ID);
}

#[test]
fn get_downcast_ref() {
    let mut registry = component::Registry::default();
    let component = Box::<FoobarComponent>::default() as Box<dyn Component<ExampleApp>>;
    registry.register(vec![component]).unwrap();

    {
        let foo_mut = registry.get_downcast_mut::<FoobarComponent>().unwrap();
        foo_mut.set_state("mutated!");
    }

    {
        let foo_comp = registry.get_downcast_ref::<FoobarComponent>().unwrap();
        assert_eq!(foo_comp.state.as_ref().unwrap(), "mutated!");
    }
}

#[test]
fn dependency_injection() {
    let mut registry = component::Registry::default();
    let mut components = init_components();

    // Start component up in reverse order to make sure sorting works
    components.reverse();

    registry.register(components).unwrap();
    registry.after_config(&ExampleConfig::default()).unwrap();

    let foobar_comp = registry.get_downcast_ref::<FoobarComponent>().unwrap();
    assert_eq!(foobar_comp.state.as_ref().unwrap(), "hijacked!");

    let quux_comp = registry.get_downcast_ref::<QuuxComponent>().unwrap();
    assert_eq!(
        quux_comp.foobar_state.as_ref().unwrap(),
        "original foobar state"
    );
}