94 lines
2.9 KiB
Rust
94 lines
2.9 KiB
Rust
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<PyAny>);
|
|
|
|
impl<'a, 'py> FromPyObject<'a, 'py> for ServiceRegistry {
|
|
type Error = TypeByNameValidationError;
|
|
|
|
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
|
|
// 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<CallServiceError> 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<ServiceData = ServiceData>,
|
|
context: Option<Context<Event>>,
|
|
target: Option<Target>,
|
|
return_response: bool,
|
|
) -> Result<ServiceResponse, CallServiceError> {
|
|
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)
|
|
}
|
|
}
|