1use std::{
2 any::TypeId,
3 collections::HashMap,
4 future::Future,
5 marker::PhantomData,
6 ops::Deref,
7 sync::{Arc, Weak},
8};
9
10use futures::FutureExt;
11use tokio_util::sync::CancellationToken;
12
13use crate::{
14 Error, Result, ServiceHandle, ServiceKey,
15 runtime::{EventCallback, QueryCallback, Runtime},
16 scope::ScopeInner,
17 service::{ServiceEntry, ServiceId, boxed_service},
18};
19
20#[derive(Clone)]
22pub struct Context {
23 pub(crate) runtime: Arc<Runtime>,
24 scope_id: u64,
25 isolations: Arc<HashMap<TypeId, u64>>,
26}
27
28impl Context {
29 pub(crate) fn root(runtime: Arc<Runtime>) -> Self {
30 Self {
31 runtime,
32 scope_id: 0,
33 isolations: Arc::new(HashMap::new()),
34 }
35 }
36
37 pub(crate) fn for_scope(runtime: Arc<Runtime>, scope_id: u64) -> Self {
38 Self {
39 runtime,
40 scope_id,
41 isolations: Arc::new(HashMap::new()),
42 }
43 }
44
45 pub fn scope_id(&self) -> u64 {
46 self.scope_id
47 }
48
49 pub fn child(&self) -> Self {
51 Self {
52 runtime: self.runtime.clone(),
53 scope_id: self.runtime.next_id(),
54 isolations: self.isolations.clone(),
55 }
56 }
57
58 pub fn isolate<K: ServiceKey>(&self) -> Self {
61 let mut isolations = (*self.isolations).clone();
62 isolations.insert(TypeId::of::<K>(), self.runtime.next_id());
63 Self {
64 runtime: self.runtime.clone(),
65 scope_id: self.scope_id,
66 isolations: Arc::new(isolations),
67 }
68 }
69
70 fn service_id<K: ServiceKey>(&self) -> ServiceId {
71 ServiceId {
72 key: TypeId::of::<K>(),
73 isolation: self
74 .isolations
75 .get(&TypeId::of::<K>())
76 .copied()
77 .unwrap_or(0),
78 }
79 }
80
81 pub fn get<K: ServiceKey>(&self) -> Result<Arc<K::Value>> {
82 let services = self.runtime.services.lock().expect("service lock poisoned");
83 let entry = services
84 .get(&self.service_id::<K>())
85 .filter(|entry| entry.active || entry.owner == self.scope_id)
86 .ok_or(Error::MissingService { name: K::NAME })?;
87 entry
88 .value
89 .downcast_ref::<Arc<K::Value>>()
90 .cloned()
91 .ok_or(Error::ServiceTypeMismatch { name: entry.name })
92 }
93
94 pub fn try_get<K: ServiceKey>(&self) -> Option<Arc<K::Value>> {
95 let services = self.runtime.services.lock().expect("service lock poisoned");
96 let entry = services.get(&self.service_id::<K>())?;
97 if !entry.active && entry.owner != self.scope_id {
98 return None;
99 }
100 entry.value.downcast_ref::<Arc<K::Value>>().cloned()
101 }
102
103 pub async fn emit<E: Event>(&self, event: E) -> Result<()> {
105 self.runtime.emit_serial(event).await
106 }
107
108 pub async fn parallel<E: Event>(&self, event: E) -> Result<()> {
110 self.runtime.emit_parallel(event).await
111 }
112
113 pub async fn query<Q: Query>(&self, query: Q) -> Result<Option<Q::Response>> {
115 self.runtime.query(query).await
116 }
117}
118
119#[derive(Clone, Copy, Debug)]
120pub struct Ready;
121
122#[derive(Clone, Copy, Debug)]
123pub struct Fork {
124 pub plugin: crate::PluginId,
125 pub activation: crate::ActivationId,
126}
127
128#[derive(Clone, Copy, Debug)]
129pub struct Dispose {
130 pub plugin: crate::PluginId,
131 pub activation: crate::ActivationId,
132}
133
134pub trait Event: Send + Sync + 'static {}
136impl<T: Send + Sync + 'static> Event for T {}
137
138pub trait Query: Send + Sync + 'static {
140 type Response: Send + Sync + 'static;
141}
142
143#[derive(Clone)]
146pub struct PluginContext {
147 context: Context,
148 scope: Arc<ScopeInner>,
149}
150
151impl PluginContext {
152 pub(crate) fn new(context: Context, scope: Arc<ScopeInner>) -> Self {
153 Self { context, scope }
154 }
155
156 pub fn isolate<K: ServiceKey>(&self) -> Self {
157 Self {
158 context: self.context.isolate::<K>(),
159 scope: self.scope.clone(),
160 }
161 }
162
163 pub fn provide<K: ServiceKey>(&self, value: Arc<K::Value>) -> Result<ServiceHandle<K>> {
164 let id = self.context.service_id::<K>();
165 let token = self.context.runtime.next_service_generation();
166 let generation = self.context.runtime.next_service_generation();
167 {
168 let mut services = self
169 .context
170 .runtime
171 .services
172 .lock()
173 .expect("service lock poisoned");
174 if services.contains_key(&id) {
175 return Err(Error::DuplicateService { name: K::NAME });
176 }
177 services.insert(
178 id,
179 ServiceEntry {
180 value: boxed_service::<K>(value),
181 owner: self.scope.id,
182 token,
183 generation,
184 name: K::NAME,
185 active: false,
186 },
187 );
188 }
189
190 let weak = Arc::downgrade(&self.context.runtime);
191 let owner = self.scope.id;
192 if let Err(error) = self.scope.push(Box::new(move || {
193 Box::pin(async move {
194 if let Some(runtime) = weak.upgrade() {
195 runtime.remove_service(id, owner, token);
196 }
197 Ok(())
198 })
199 })) {
200 self.context.runtime.remove_service(id, owner, token);
201 return Err(error);
202 }
203
204 Ok(ServiceHandle {
205 id,
206 runtime: Arc::downgrade(&self.context.runtime),
207 owner,
208 token,
209 _key: PhantomData,
210 })
211 }
212
213 pub fn on<E, H, Fut>(&self, handler: H) -> Result<ListenerHandle>
214 where
215 E: Event,
216 H: Fn(Context, Arc<E>) -> Fut + Send + Sync + 'static,
217 Fut: Future<Output = Result<()>> + Send + 'static,
218 {
219 let event_key = TypeId::of::<E>();
220 let context = self.context.clone();
221 let callback: Arc<EventCallback> = Arc::new(move |event| {
222 let result = event.downcast::<E>();
223 let context = context.clone();
224 match result {
225 Ok(event) => handler(context, event).boxed(),
226 Err(_) => {
227 async { Err(Error::Cleanup("event payload type mismatch".into())) }.boxed()
228 }
229 }
230 });
231 let id = self
232 .context
233 .runtime
234 .add_listener(event_key, self.scope.id, callback);
235 let weak = Arc::downgrade(&self.context.runtime);
236 if let Err(error) = self.scope.push(Box::new(move || {
237 Box::pin(async move {
238 if let Some(runtime) = weak.upgrade() {
239 runtime.remove_listener(event_key, id);
240 }
241 Ok(())
242 })
243 })) {
244 self.context.runtime.remove_listener(event_key, id);
245 return Err(error);
246 }
247 Ok(ListenerHandle {
248 runtime: Arc::downgrade(&self.context.runtime),
249 key: event_key,
250 id,
251 query: false,
252 })
253 }
254
255 pub fn on_query<Q, H, Fut>(&self, handler: H) -> Result<ListenerHandle>
256 where
257 Q: Query,
258 H: Fn(Context, Arc<Q>) -> Fut + Send + Sync + 'static,
259 Fut: Future<Output = Result<Option<Q::Response>>> + Send + 'static,
260 {
261 let key = TypeId::of::<Q>();
262 let context = self.context.clone();
263 let callback: Arc<QueryCallback> = Arc::new(move |query| {
264 let result = query.downcast::<Q>();
265 let context = context.clone();
266 match result {
267 Ok(query) => handler(context, query)
268 .map(|result| {
269 result.map(|response| {
270 response.map(|value| {
271 Box::new(value) as Box<dyn std::any::Any + Send + Sync>
272 })
273 })
274 })
275 .boxed(),
276 Err(_) => {
277 async { Err(Error::Cleanup("query payload type mismatch".into())) }.boxed()
278 }
279 }
280 });
281 let id = self
282 .context
283 .runtime
284 .add_query_listener(key, self.scope.id, callback);
285 let weak = Arc::downgrade(&self.context.runtime);
286 if let Err(error) = self.scope.push(Box::new(move || {
287 Box::pin(async move {
288 if let Some(runtime) = weak.upgrade() {
289 runtime.remove_query_listener(key, id);
290 }
291 Ok(())
292 })
293 })) {
294 self.context.runtime.remove_query_listener(key, id);
295 return Err(error);
296 }
297 Ok(ListenerHandle {
298 runtime: Arc::downgrade(&self.context.runtime),
299 key,
300 id,
301 query: true,
302 })
303 }
304
305 pub fn defer<F, Fut>(&self, cleanup: F) -> Result<()>
306 where
307 F: FnOnce() -> Fut + Send + 'static,
308 Fut: Future<Output = Result<()>> + Send + 'static,
309 {
310 self.scope.push(Box::new(move || Box::pin(cleanup())))
311 }
312
313 pub fn manage<R: Resource>(&self, resource: R) -> Result<()> {
314 let resource = Arc::new(std::sync::Mutex::new(Some(resource)));
315 let start_resource = resource.clone();
316 self.scope.on_commit(Box::new(move || {
317 Box::pin(async move {
318 let resource = start_resource
319 .lock()
320 .expect("resource lock poisoned")
321 .take()
322 .ok_or_else(|| Error::cleanup("resource already consumed"))?;
323 let result = resource.start().await;
324 *start_resource.lock().expect("resource lock poisoned") = Some(resource);
325 result
326 })
327 }))?;
328
329 self.scope.push(Box::new(move || {
330 Box::pin(async move {
331 let resource = resource.lock().expect("resource lock poisoned").take();
332 if let Some(resource) = resource {
333 resource.cancel();
334 Box::new(resource).dispose().await?;
335 }
336 Ok(())
337 })
338 }))
339 }
340
341 pub fn spawn<F, Fut>(&self, task: F) -> Result<TaskHandle>
344 where
345 F: FnOnce(CancellationToken) -> Fut + Send + 'static,
346 Fut: Future<Output = Result<()>> + Send + 'static,
347 {
348 let token = CancellationToken::new();
349 let join = Arc::new(std::sync::Mutex::new(None));
350 let start_join = join.clone();
351 let child = token.child_token();
352 self.scope.on_commit(Box::new(move || {
353 Box::pin(async move {
354 let future = std::panic::AssertUnwindSafe(task(child))
355 .catch_unwind()
356 .map(|result| match result {
357 Ok(result) => result,
358 Err(payload) => Err(Error::panic(payload)),
359 });
360 *start_join.lock().expect("task lock poisoned") = Some(tokio::spawn(future));
361 Ok(())
362 })
363 }))?;
364
365 let cleanup_token = token.clone();
366 self.scope.push(Box::new(move || {
367 Box::pin(async move {
368 cleanup_token.cancel();
369 let join = join.lock().expect("task lock poisoned").take();
370 if let Some(mut join) = join {
371 match tokio::time::timeout(std::time::Duration::from_secs(5), &mut join).await {
372 Ok(result) => result??,
373 Err(_) => {
374 join.abort();
375 let _ = join.await;
376 return Err(Error::TaskTimeout { seconds: 5 });
377 }
378 }
379 }
380 Ok(())
381 })
382 }))?;
383 Ok(TaskHandle { token })
384 }
385}
386
387impl Deref for PluginContext {
388 type Target = Context;
389 fn deref(&self) -> &Self::Target {
390 &self.context
391 }
392}
393
394pub struct ListenerHandle {
396 runtime: Weak<Runtime>,
397 key: TypeId,
398 id: u64,
399 query: bool,
400}
401
402impl ListenerHandle {
403 pub fn cancel(&self) -> bool {
404 self.runtime.upgrade().is_some_and(|runtime| {
405 if self.query {
406 runtime.remove_query_listener(self.key, self.id)
407 } else {
408 runtime.remove_listener(self.key, self.id)
409 }
410 })
411 }
412}
413
414pub struct TaskHandle {
415 token: CancellationToken,
416}
417impl TaskHandle {
418 pub fn cancel(&self) {
419 self.token.cancel();
420 }
421}
422
423pub trait Resource: Send + Sync + 'static {
424 fn start(&self) -> impl Future<Output = Result<()>> + Send {
425 async { Ok(()) }
426 }
427
428 fn cancel(&self) {}
429
430 fn dispose(self: Box<Self>) -> impl Future<Output = Result<()>> + Send;
431}