Skip to main content

ratatui_kit/
context.rs

1// 上下文模块,提供全局/局部依赖注入能力,支持跨组件数据共享与生命周期管理。
2//
3// ## 主要类型
4// - [`Context`]:通用上下文枚举,支持所有权、不可变/可变引用三种模式。
5// - [`ContextStack`]:上下文栈,支持嵌套作用域和动态查找。
6// - [`SystemContext`]:系统级上下文,控制全局退出等。
7
8use std::{
9    any::{Any, TypeId},
10    cell::{Ref, RefCell, RefMut},
11};
12
13// 通用上下文类型,支持所有权、不可变引用、可变引用三种模式。
14pub enum Context<'a> {
15    Ref(&'a dyn Any),
16    Mut(&'a mut dyn Any),
17    Owned(Box<dyn Any>),
18}
19
20impl<'a> Context<'a> {
21    // 创建一个拥有所有权的上下文。
22    pub fn owned<T: Any>(context: T) -> Self {
23        Context::Owned(Box::new(context))
24    }
25
26    // 创建一个不可变引用的上下文。
27    pub fn from_ref<T: Any>(context: &'a T) -> Self {
28        Context::Ref(context)
29    }
30
31    // 创建一个可变引用的上下文。
32    pub fn from_mut<T: Any>(context: &'a mut T) -> Self {
33        Context::Mut(context)
34    }
35
36    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
37        match self {
38            Context::Ref(context) => context.downcast_ref(),
39            Context::Mut(context) => context.downcast_ref(),
40            Context::Owned(context) => context.downcast_ref(),
41        }
42    }
43
44    pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
45        match self {
46            Context::Ref(_) => None,
47            Context::Mut(context) => context.downcast_mut(),
48            Context::Owned(context) => context.downcast_mut(),
49        }
50    }
51
52    pub fn borrow(&'_ mut self) -> Context<'_> {
53        match self {
54            Context::Ref(context) => Context::Ref(*context),
55            Context::Mut(context) => Context::Mut(*context),
56            Context::Owned(context) => Context::Mut(&mut **context),
57        }
58    }
59
60    fn type_id(&self) -> TypeId {
61        match self {
62            Context::Ref(context) => (*context).type_id(),
63            Context::Mut(context) => (**context).type_id(),
64            Context::Owned(context) => (**context).type_id(),
65        }
66    }
67}
68
69struct ContextEntry<'a> {
70    type_id: TypeId,
71    context: RefCell<Context<'a>>,
72}
73
74impl<'a> ContextEntry<'a> {
75    fn new(context: Context<'a>) -> Self {
76        Self {
77            type_id: context.type_id(),
78            context: RefCell::new(context),
79        }
80    }
81}
82
83// `ContextStack` 查找结果——区分三态,使断言型 `use_context` 给出精确诊断,
84// 而 `try_use_context` 能安全降级为 `None`(不 panic)。
85pub(crate) enum ContextLookup<R> {
86    // 找到且成功借用。
87    Found(R),
88    // 类型匹配但当前已被借用(持守卫重入,属编程错误)。
89    AlreadyBorrowed,
90    // 栈中无该类型 context。
91    NotFound,
92}
93
94pub struct ContextStack<'a> {
95    stack: Vec<ContextEntry<'a>>,
96}
97
98impl<'a> ContextStack<'a> {
99    pub(crate) fn root(root_context: &'a mut dyn Any) -> Self {
100        ContextStack {
101            stack: vec![ContextEntry::new(Context::Mut(root_context))],
102        }
103    }
104    // 在上下文栈中临时插入一个新的上下文,并在闭包 f 执行期间可用。
105    pub(crate) fn with_context<'b, F>(&'b mut self, context: Option<Context<'b>>, f: F)
106    where
107        F: FnOnce(&mut ContextStack),
108    {
109        if let Some(context) = context {
110            // SAFETY: 可变引用在生命周期上是不变的,为了插入更短生命周期的上下文,需要对 'a 进行转变。
111            // 只有在不允许对栈进行其他更改,并且在调用后立即恢复栈的情况下才是安全的。
112            let shorter_lived_self =
113                unsafe { std::mem::transmute::<&mut Self, &mut ContextStack<'b>>(self) };
114            shorter_lived_self.stack.push(ContextEntry::new(context));
115            f(shorter_lived_self);
116            shorter_lived_self.stack.pop();
117        } else {
118            f(self);
119        };
120    }
121
122    pub(crate) fn get_context<T: Any>(&'_ self) -> ContextLookup<Ref<'_, T>> {
123        let expected_type_id = TypeId::of::<T>();
124        for entry in self.stack.iter().rev() {
125            if entry.type_id != expected_type_id {
126                continue;
127            }
128
129            let Ok(context) = entry.context.try_borrow() else {
130                return ContextLookup::AlreadyBorrowed;
131            };
132
133            if let Ok(res) = Ref::filter_map(context, |context| context.downcast_ref::<T>()) {
134                return ContextLookup::Found(res);
135            }
136        }
137        ContextLookup::NotFound
138    }
139
140    pub(crate) fn get_context_mut<T: Any>(&'_ self) -> ContextLookup<RefMut<'_, T>> {
141        let expected_type_id = TypeId::of::<T>();
142        for entry in self.stack.iter().rev() {
143            if entry.type_id != expected_type_id {
144                continue;
145            }
146
147            let Ok(context) = entry.context.try_borrow_mut() else {
148                return ContextLookup::AlreadyBorrowed;
149            };
150
151            if let Ok(res) = RefMut::filter_map(context, |context| context.downcast_mut::<T>()) {
152                return ContextLookup::Found(res);
153            }
154        }
155        ContextLookup::NotFound
156    }
157}
158
159pub struct SystemContext {
160    should_exit: bool,
161    auto_quit_on_ctrl_c: bool,
162    // 中央输入事件运行时。组件经 `get_context_mut::<SystemContext>().input` 登记层/handler,
163    // 渲染循环经 `system_context.input.dispatch(event)` 分发。运行时单线程,无需 Send + Sync。
164    pub(crate) input: crate::input::InputRuntime,
165}
166
167impl SystemContext {
168    pub(crate) fn new() -> Self {
169        Self {
170            should_exit: false,
171            auto_quit_on_ctrl_c: true,
172            input: crate::input::InputRuntime::default(),
173        }
174    }
175
176    pub(crate) fn should_exit(&self) -> bool {
177        self.should_exit
178    }
179
180    pub fn exit(&mut self) {
181        self.should_exit = true;
182    }
183
184    /// 设置收到 Ctrl+C 时是否由渲染循环直接退出。
185    ///
186    /// 默认为 `true`。设为 `false` 后,Ctrl+C 会进入中央事件分发器,由应用层
187    /// handler 实现取消任务、二次确认退出等行为。
188    pub fn set_auto_quit_on_ctrl_c(&mut self, enabled: bool) {
189        self.auto_quit_on_ctrl_c = enabled;
190    }
191
192    pub(crate) fn auto_quit_on_ctrl_c(&self) -> bool {
193        self.auto_quit_on_ctrl_c
194    }
195}