use std::future::Future;
use std::pin::Pin;
use tokio::sync::oneshot;
use crate::bus::{BusRpcContext, OpaqueRequestEnvelope};
use super::context::ConstructionFacade;
pub type DomainFuture<'a> = Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
pub struct DomainCallbacks<Bus: ?Sized> {
pub new: for<'facade, 'bus> fn(&'facade mut ConstructionFacade<'bus, Bus>) -> DomainFuture<'facade>,
pub start: fn(BusRpcContext) -> DomainFuture<'static>,
pub stop: fn(BusRpcContext) -> DomainFuture<'static>,
}
pub trait DomainDescriptorFactory<Bus: ?Sized>: Send + Sync + 'static {
fn callbacks() -> DomainCallbacks<Bus>;
}
pub struct DomainDescriptor<Bus: ?Sized> {
name: &'static str,
callbacks: DomainCallbacks<Bus>,
}
impl<Bus: ?Sized> DomainDescriptor<Bus> {
pub const fn from_callbacks(name: &'static str, callbacks: DomainCallbacks<Bus>) -> Self {
Self { name, callbacks }
}
pub fn for_domain<Domain>(name: &'static str) -> Self
where
Domain: DomainDescriptorFactory<Bus>,
{
Self::from_callbacks(name, Domain::callbacks())
}
pub const fn name(&self) -> &'static str {
self.name
}
pub(crate) fn construct<'facade, 'bus>(
&self,
facade: &'facade mut ConstructionFacade<'bus, Bus>,
) -> DomainFuture<'facade> {
(self.callbacks.new)(facade)
}
pub(crate) fn start(&self, context: BusRpcContext) -> DomainFuture<'static> {
(self.callbacks.start)(context)
}
pub(crate) fn stop(&self, context: BusRpcContext) -> DomainFuture<'static> {
(self.callbacks.stop)(context)
}
}
pub async fn dispatch_lifecycle_request<Marker, Request, Response, DomainError>(
context: BusRpcContext,
request: impl FnOnce(oneshot::Sender<Result<Response, DomainError>>) -> Request,
) -> Option<Result<Response, DomainError>>
where
Marker: 'static,
Request: Send + 'static,
Response: Send + 'static,
DomainError: Send + 'static,
{
let (reply_to, reply) = oneshot::channel();
if context
.bus_sender()
.expect("authenticated lifecycle context")
.send(OpaqueRequestEnvelope::new::<Marker, _>(
context.jwt().expect("authenticated lifecycle context"),
request(reply_to),
))
.await
.is_err()
{
return None;
}
reply.await.ok()
}