use super::{event::context::context::Context, service::IntoServiceCall}; use pyo3::{ exceptions::{PyException, PyTypeError}, prelude::*, }; use python_utils::{detach, validate_type_by_name, TypeByNameValidationError}; use snafu::{ResultExt, Snafu}; #[derive(Debug)] pub struct ServiceRegistry(Py); impl<'a, 'py> FromPyObject<'a, 'py> for ServiceRegistry { type Error = TypeByNameValidationError; fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result { // region: Validation validate_type_by_name(&ob, "ServiceRegistry")?; // endregion: Validation Ok(Self(detach(ob))) } } #[derive(Debug, Snafu)] pub enum CallServiceError { /// couldn't successfully call `async_call` and turn it into a `Future` CallIntoFutureError { source: PyErr }, /// couldn't await the `Future` from the `async_call` AwaitFutureError { source: PyErr }, /// couldn't extract the service response as the requested type ExtractServiceResponseError { source: PyErr }, } impl From for PyErr { fn from(error: CallServiceError) -> Self { match &error { CallServiceError::CallIntoFutureError { .. } => PyException::new_err(error.to_string()), CallServiceError::AwaitFutureError { .. } => PyException::new_err(error.to_string()), CallServiceError::ExtractServiceResponseError { .. } => { PyTypeError::new_err(error.to_string()) } } } } impl ServiceRegistry { pub async fn call_service< 'a, ServiceData: for<'py> IntoPyObject<'py>, Target: for<'py> IntoPyObject<'py>, Event: for<'py> IntoPyObject<'py>, ServiceResponse: 'static + for<'py> FromPyObjectOwned<'py>, >( &'a self, service_call: impl IntoServiceCall, context: Option>, target: Option, return_response: bool, ) -> Result { let (domain, service, service_data) = service_call.into_service_call(); let blocking = true; let args = ( domain, service, service_data, blocking, context, target, return_response, ); let future = Python::attach::<_, PyResult<_>>(|py| { let service_registry = self.0.bind(py); let awaitable = service_registry.call_method("async_call", args, None)?; pyo3_async_runtimes::tokio::into_future(awaitable) }) .context(CallIntoFutureSnafu)?; let service_response = future.await.context(AwaitFutureSnafu)?; let service_response = Python::attach(move |py| { service_response .extract(py) .map_err(Into::into) .context(ExtractServiceResponseSnafu) })?; Ok(service_response) } }