misty_vm/
controllers.rs

1use std::{any::Any, sync::Arc};
2
3use crate::{
4    client::{MistyClientHandle, MistyClientInner},
5    resources::ResourceUpdateAction,
6    states::GuardCleanupStatesForPanic,
7};
8
9pub struct MistyControllerContext<'a> {
10    pub(crate) handle: MistyClientHandle<'a>,
11}
12
13impl<'a> MistyControllerContext<'a> {
14    pub(crate) fn new(handle: MistyClientHandle<'a>) -> Self {
15        Self { handle }
16    }
17
18    pub fn handle(&self) -> MistyClientHandle {
19        self.handle
20    }
21}
22
23pub trait MistyController<Arg, E> {
24    fn call(&self, ctx: MistyControllerContext, arg: Arg) -> Result<(), E>;
25}
26
27impl<Arg, E, F> MistyController<Arg, E> for F
28where
29    F: Fn(MistyControllerContext, Arg) -> Result<(), E>,
30{
31    fn call(&self, ctx: MistyControllerContext, arg: Arg) -> Result<(), E> {
32        self(ctx, arg)
33    }
34}
35
36pub struct ControllerRet<R> {
37    pub changed_view: Option<R>,
38    pub changed_resources: Vec<ResourceUpdateAction>,
39}
40
41pub(crate) fn call_controller<R, Controller, Arg, E>(
42    inner: &Arc<MistyClientInner>,
43    controller: Controller,
44    arg: Arg,
45) -> Result<ControllerRet<R>, E>
46where
47    R: Any + Default + Send + Sync + 'static,
48    Controller: MistyController<Arg, E>,
49{
50    let controller_name = std::any::type_name::<Controller>();
51    let span = tracing::span!(tracing::Level::DEBUG, "call controller", controller_name);
52    let _span_guard = span.enter();
53
54    let mut _cleanup_guard = GuardCleanupStatesForPanic::new(Arc::downgrade(inner));
55
56    let ctx = MistyControllerContext::new(MistyClientHandle { inner: &inner });
57    inner.state_manager.enter_mut_span();
58    let res = controller.call(ctx, arg);
59    let can_notify = inner.state_manager.leave_mut_span();
60
61    let mut changed_view: Option<R> = None;
62    let mut changed_actions: Vec<ResourceUpdateAction> = Default::default();
63
64    if can_notify {
65        changed_actions = inner.resource_manager.take_all_actions();
66        changed_view = Some(inner.view_manager.build_view(&inner).cast::<R>());
67        inner.state_manager.clear_updated_states();
68    }
69
70    _cleanup_guard.mark();
71
72    if let Err(e) = res {
73        return Err(e);
74    }
75    Ok(ControllerRet {
76        changed_view,
77        changed_resources: changed_actions,
78    })
79}