1use std::{
9 any::{Any, TypeId},
10 cell::{Ref, RefCell, RefMut},
11};
12
13pub 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 pub fn owned<T: Any>(context: T) -> Self {
23 Context::Owned(Box::new(context))
24 }
25
26 pub fn from_ref<T: Any>(context: &'a T) -> Self {
28 Context::Ref(context)
29 }
30
31 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
83pub(crate) enum ContextLookup<R> {
86 Found(R),
88 AlreadyBorrowed,
90 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 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 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 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 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}