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
use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

use crate::flag::Flag;

pub struct Platform {
    services: Mutex<HashMap<String, Arc<dyn Any + Send + Sync>>>,
    pub is_running: Flag,
}

impl Platform {
    pub fn new() -> Arc<Self> {
        Arc::new(Platform {
            services: Mutex::new(HashMap::new()),
            is_running: Flag::new(true),
        })
    }

    pub fn register<T>(&self, service: Arc<T>)
    where
        T: Any + Send + Sync,
    {
        let name = std::any::type_name::<T>().to_owned();
        self.services.lock().unwrap().insert(name, service);
    }

    pub fn find<T>(&self) -> Option<Arc<T>>
    where
        T: Any + Send + Sync,
    {
        let name = std::any::type_name::<T>();
        let services = self.services.lock().unwrap();
        services
            .get(name)
            .and_then(|entry| entry.clone().downcast::<T>().ok())
    }

    pub fn require<T>(&self) -> Arc<T>
    where
        T: Any + Send + Sync,
    {
        match self.find::<T>() {
            Some(service) => service,
            None => panic!(
                "A required component ({}) was not available in the platform registry!",
                std::any::type_name::<T>()
            ),
        }
    }

    pub fn is_running(&self) -> bool {
        self.is_running.read()
    }

    pub fn terminate(&self) {
        self.is_running.change(false);
    }
}

#[cfg(test)]
mod tests {
    use std::sync::Arc;

    use crate::platform::Platform;

    struct Foo {
        value: i32,
    }

    struct Bar {
        value: i32,
    }

    #[test]
    fn registering_services_works() {
        let platform = Platform::new();
        platform.register(Arc::new(Foo { value: 42 }));
        platform.register(Arc::new(Bar { value: 32 }));

        let foo = platform.find::<Foo>();
        assert_eq!(foo.is_some(), true);
        assert_eq!(foo.unwrap().value, 42);

        let bar = platform.require::<Bar>();
        assert_eq!(bar.value, 32);

        assert_eq!(platform.find::<i32>(), None);
    }

    #[test]
    #[should_panic]
    fn requiring_an_unknown_service_panics() {
        let platform = Platform::new();
        platform.require::<i32>();
    }
}