misty_vm/client/
pod.rs

1use std::{
2    any::Any,
3    convert::Infallible,
4    marker::PhantomData,
5    sync::{atomic::AtomicBool, Arc, RwLock},
6};
7
8use once_cell::sync::Lazy;
9
10use crate::{
11    async_task::{IAsyncTaskRuntimeAdapter, MistyAsyncTaskPools},
12    controllers::{call_controller, ControllerRet, MistyController},
13    resources::MistyResourceManager,
14    schedule::{controller_flush_scheduled_tasks, ScheduleManager},
15    services::MistyServiceManager,
16    signals::{MistySignal, SignalEmitter},
17    states::MistyStateManager,
18    views::MistyViewModelManager,
19};
20
21use super::{MistyClientAccessor, MistyClientId, MistyClientInner};
22
23struct MistyClient<R> {
24    inner: Arc<MistyClientInner>,
25    _marker: PhantomData<R>,
26}
27
28impl<R> MistyClient<R>
29where
30    R: Any + Default + Send + Sync + 'static,
31{
32    fn new(
33        view_manager: MistyViewModelManager<R>,
34        state_manager: MistyStateManager,
35        service_manager: MistyServiceManager,
36        async_task_runtime: impl IAsyncTaskRuntimeAdapter + Send + Sync + 'static,
37    ) -> Self {
38        let inner = Arc::new(MistyClientInner {
39            id: MistyClientId::alloc(),
40            state_manager,
41            view_manager: Box::new(view_manager),
42            service_manager,
43            resource_manager: MistyResourceManager::new(),
44            async_task_pools: MistyAsyncTaskPools::new(),
45            async_task_runtime: Box::new(async_task_runtime),
46            schedule_manager: ScheduleManager::new(),
47            signal_emitter: SignalEmitter::new(),
48            destroyed: AtomicBool::new(false),
49        });
50
51        Self {
52            inner,
53            _marker: Default::default(),
54        }
55    }
56}
57
58pub struct SingletonMistyClientPod<R> {
59    client: Lazy<RwLock<Option<MistyClient<R>>>>,
60}
61
62impl<R> SingletonMistyClientPod<R>
63where
64    R: Any + Default + Send + Sync + 'static,
65{
66    pub const fn new() -> Self {
67        Self {
68            client: Lazy::new(|| Default::default()),
69        }
70    }
71
72    pub fn reset(&self) {
73        let _ = tracing::span!(tracing::Level::INFO, "SingletonMistyClientPod.reset").enter();
74        let pod = self.client.write().unwrap().take();
75        if let Some(client) = pod {
76            client.inner.destroy();
77        }
78    }
79
80    pub fn destroy(&self) {
81        self.inner().destroy();
82    }
83
84    pub fn create(
85        &self,
86        view_manager: MistyViewModelManager<R>,
87        state_manager: MistyStateManager,
88        service_manager: MistyServiceManager,
89        async_task_runtime: impl IAsyncTaskRuntimeAdapter + Send + Sync + 'static,
90    ) {
91        let _ = tracing::span!(tracing::Level::INFO, "SingletonMistyClientPod.set").enter();
92
93        let mut pod = self.client.write().unwrap();
94        if pod.is_some() {
95            panic!("client is already in pod");
96        }
97        *pod = Some(MistyClient::new(
98            view_manager,
99            state_manager,
100            service_manager,
101            async_task_runtime,
102        ));
103    }
104
105    pub fn call_controller<Controller, Arg, E>(
106        &self,
107        controller: Controller,
108        arg: Arg,
109    ) -> Result<ControllerRet<R>, E>
110    where
111        Controller: MistyController<Arg, E>,
112    {
113        let inner = {
114            let pod = self.client.read().unwrap();
115            if let Some(client) = pod.as_ref() {
116                client.inner.clone()
117            } else {
118                panic!(
119                    "client not in singleton pod. controller is {}",
120                    std::any::type_name::<Controller>()
121                );
122            }
123        };
124        call_controller(&inner, controller, arg)
125    }
126
127    pub fn on_signal(&self, f: impl Fn(MistySignal) + Send + Sync + 'static) {
128        let inner = self.inner();
129        inner.signal_emitter.set(f);
130    }
131
132    pub fn flush_scheduled_tasks(&self) -> Result<ControllerRet<R>, Infallible> {
133        self.call_controller(controller_flush_scheduled_tasks, ())
134    }
135
136    pub fn accessor(&self) -> MistyClientAccessor {
137        MistyClientAccessor {
138            inner: Arc::downgrade(&self.inner()),
139        }
140    }
141
142    fn inner(&self) -> Arc<MistyClientInner> {
143        let pod = self.client.read().unwrap();
144        if let Some(client) = pod.as_ref() {
145            client.inner.clone()
146        } else {
147            panic!("client not in singleton pod.");
148        }
149    }
150}