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}