tnn/extension/
extension_context.rs1use anyhow::Result;
2use std::{future::Future, sync::Arc};
3use thiserror::Error;
4use tokio::sync::Mutex;
5
6use super::{
7	repository::{AddCallArgs, Repository, ADD_CALL},
8	Call, CallContext, CallOutput, Mixin, State,
9};
10
11#[repr(C)]
12pub struct ExtensionContext {
13	extension_name: &'static str,
14	locked: Arc<Mutex<bool>>,
16	repository: Repository,
17	pub state: Arc<Mutex<State>>,
18}
19
20impl ExtensionContext {
21	pub fn new(state: Arc<Mutex<State>>, extension_name: &'static str, repository: Repository) -> Self {
22		Self {
23			extension_name,
24			locked: Arc::new(Mutex::new(false)),
25			repository,
26			state,
27		}
28	}
29
30	async fn _lock(&self) {
31		let mut locked = self.locked.lock().await;
32		*locked = true;
33	}
34
35	pub async fn is_locked(&self) -> bool {
36		*self.locked.lock().await
37	}
38
39	async fn assert_not_locked(&self) -> Result<()> {
40		if self.is_locked().await {
41			Err(LockedError.into())
42		} else {
43			Ok(())
44		}
45	}
46
47	pub async fn add_mixin<Payload, ReturnType: Future<Output = Result<()>>>(
48		&self,
49		_mixin: Mixin<Payload>,
50		_subscription_approver: &'static impl Fn(CallContext<&'static str>) -> ReturnType,
51	) -> Result<()> {
52		self.assert_not_locked().await?;
53
54		todo!("James Bradlee: implement this")
55	}
56
57	pub async fn emit<Payload>(&self, _mixin: Mixin<Payload>, _payload: Payload) -> Result<()> {
58		todo!("James Bradlee: implement this")
59	}
60
61	pub async fn emit_some<Payload>(
62		_mixin: Mixin<Payload>,
63		_payload: Payload,
64		_receivers: &Vec<&'static str>,
65	) -> Result<()> {
66		todo!("James Bradlee: implement this")
67	}
68
69	pub async fn call<Argument, Return>(
70		&self,
71		call: &'static Call<Argument, Return>,
72		argument: Argument,
73	) -> Result<Return> {
74		let state = self.repository.0.get_dependency_state(call.owner).await?;
75
76		unsafe {
77			self.repository
78				.0
79				.get_handler_for_call(call.owner, call.id)
80				.await?
81				.invoke(CallContext::new(state, self.extension_name, argument))
82		}
83		.await
84	}
85
86	pub fn add_call<Argument, Return, Handler: Fn(CallContext<Argument>) -> CallOutput<Return>>(
87		&self,
88		call: &'static Call<Argument, Return>,
89		handler: &'static Handler,
90	) -> impl Future<Output = Result<()>> + '_ {
91		let opaque = super::opaque_fn::OpaqueFunctionCall::from(handler);
92
93		async {
94			self.assert_not_locked().await?;
95
96			self.call::<AddCallArgs, ()>(
97				&ADD_CALL,
98				AddCallArgs {
99					call: call.id,
100					handler: opaque,
101				},
102			)
103			.await
104		}
105	}
106}
107
108#[derive(Error, Debug)]
109#[error("Extension '{0}' has already registered a handler for call '{0}/{1}'")]
110pub struct DuplicateCallError(&'static str, &'static str);
111
112#[derive(Error, Debug)]
113#[error("Extension '{0}' tried to add handler for call '{1}/{2}'")]
114pub struct NotCallOwnerError(&'static str, &'static str, &'static str);
115
116#[derive(Error, Debug)]
117#[error("Extension '{0}' has already registered an approver for mixin '{0}/{1}'")]
118pub struct DuplicateMixinError(&'static str, &'static str);
119
120#[derive(Error, Debug)]
121#[error("Extension '{0}' tried to add approver for mixin '{1}/{2}'")]
122pub struct NotMixinOwnerError(&'static str, &'static str, &'static str);
123
124#[derive(Error, Debug)]
125#[error("Context is locked and cannot be modified!")]
126pub struct LockedError;
127
128#[derive(Error, Debug)]
129#[error("Extension '{0}' tried to call '{1}', but call handler could not be downcasted.")]
130pub struct CouldNotGetCallHandler(&'static str, &'static str);