use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::{Arc, OnceLock};
use crate::auth::{AuthBootOptions, AvesAuthError, ConstructionResult};
use crate::bus::BusRpcContext;
#[derive(Default)]
pub struct ConstructionInputs {
values: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
}
impl ConstructionInputs {
pub fn new() -> Self {
Self::default()
}
pub fn with<Input>(mut self, input: Input) -> Self
where
Input: Send + Sync + 'static,
{
self.values.insert(TypeId::of::<Input>(), Box::new(input));
self
}
pub(crate) fn get<Input>(&self) -> Option<&Input>
where
Input: Send + Sync + 'static,
{
self.values
.get(&TypeId::of::<Input>())
.and_then(|value| value.downcast_ref())
}
pub(crate) fn insert<Input>(&mut self, input: Input)
where
Input: Send + Sync + 'static,
{
self.values.insert(TypeId::of::<Input>(), Box::new(input));
}
}
pub struct ConstructionFacade<'bus, Bus: ?Sized> {
bus: &'bus mut Bus,
services: &'bus mut ServiceSlotRegistry,
inputs: &'bus (dyn Any + Send + Sync),
auth_options: Option<&'bus AuthBootOptions>,
auth_construction: Option<&'bus mut Option<Result<ConstructionResult, AvesAuthError>>>,
root_context: Option<BusRpcContext>,
}
impl<'bus, Bus: ?Sized> ConstructionFacade<'bus, Bus> {
pub(super) fn new_with_auth(
bus: &'bus mut Bus,
services: &'bus mut ServiceSlotRegistry,
inputs: &'bus (dyn Any + Send + Sync),
auth_options: &'bus AuthBootOptions,
auth_construction: &'bus mut Option<Result<ConstructionResult, AvesAuthError>>,
) -> Self {
Self {
bus,
services,
inputs,
auth_options: Some(auth_options),
auth_construction: Some(auth_construction),
root_context: None,
}
}
pub(super) fn new_with_root_context(
bus: &'bus mut Bus,
services: &'bus mut ServiceSlotRegistry,
inputs: &'bus (dyn Any + Send + Sync),
root_context: BusRpcContext,
) -> Self {
Self {
bus,
services,
inputs,
auth_options: None,
auth_construction: None,
root_context: Some(root_context),
}
}
pub fn root_context(&self) -> &BusRpcContext {
self.root_context
.as_ref()
.expect("root context is available for runtime domain construction")
}
pub fn bus_mut(&mut self) -> &mut Bus {
self.bus
}
pub fn auth_boot_options(&self) -> Option<&AuthBootOptions> {
self.auth_options
}
pub fn set_auth_construction(&mut self, result: Result<ConstructionResult, AvesAuthError>) {
*self
.auth_construction
.as_deref_mut()
.expect("Auth construction handoff is available only for built-in Auth") = Some(result);
}
pub fn input<Input>(&self) -> Option<&Input>
where
Input: Send + Sync + 'static,
{
self.inputs
.downcast_ref::<ConstructionInputs>()
.and_then(ConstructionInputs::get)
.or_else(|| self.inputs.downcast_ref::<Input>())
}
pub fn publisher<Service>(&mut self) -> ServicePublisher<Service>
where
Service: ?Sized + Send + Sync + 'static,
{
self.services.publisher()
}
pub fn reader<Service>(&mut self) -> ServiceReader<Service>
where
Service: ?Sized + Send + Sync + 'static,
{
self.services.reader()
}
}
pub(super) struct ServiceSlotRegistry {
slots: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
}
impl ServiceSlotRegistry {
pub fn new() -> Self {
Self { slots: HashMap::new() }
}
fn slot_or_insert<Service>(&mut self) -> Arc<OnceLock<Arc<Service>>>
where
Service: ?Sized + Send + Sync + 'static,
{
let slot = self
.slots
.entry(TypeId::of::<Service>())
.or_insert_with(|| Box::new(Arc::new(OnceLock::<Arc<Service>>::new())));
slot.downcast_ref::<Arc<OnceLock<Arc<Service>>>>()
.expect("service slot type changed for an existing service key")
.clone()
}
pub(super) fn publisher<Service>(&mut self) -> ServicePublisher<Service>
where
Service: ?Sized + Send + Sync + 'static,
{
ServicePublisher {
slot: self.slot_or_insert(),
}
}
pub(super) fn reader<Service>(&mut self) -> ServiceReader<Service>
where
Service: ?Sized + Send + Sync + 'static,
{
ServiceReader {
slot: self.slot_or_insert(),
}
}
pub(super) fn declared_reader<Service>(&self) -> ServiceReader<Service>
where
Service: ?Sized + Send + Sync + 'static,
{
let slot = self
.slots
.get(&TypeId::of::<Service>())
.and_then(|slot| slot.downcast_ref::<Arc<OnceLock<Arc<Service>>>>())
.expect("runtime service slot must be declared during domain construction")
.clone();
ServiceReader { slot }
}
}
pub struct ServicePublisher<Service: ?Sized + Send + Sync + 'static> {
slot: Arc<OnceLock<Arc<Service>>>,
}
impl<Service: ?Sized + Send + Sync + 'static> ServicePublisher<Service> {
pub fn publish(&self, service: Arc<Service>) -> Result<(), Arc<Service>> {
self.slot.set(service)
}
}
pub struct ServiceReader<Service: ?Sized + Send + Sync + 'static> {
slot: Arc<OnceLock<Arc<Service>>>,
}
impl<Service: ?Sized + Send + Sync + 'static> ServiceReader<Service> {
pub fn get(&self) -> Option<Arc<Service>> {
self.slot.get().cloned()
}
#[cfg(any(test, feature = "test-support"))]
pub fn from_published_test_service(service: Arc<Service>) -> Self {
let slot = Arc::new(OnceLock::new());
assert!(
slot.set(service).is_ok(),
"new test service slot must not already be published"
);
Self { slot }
}
}
impl<Service: ?Sized + Send + Sync + 'static> Clone for ServiceReader<Service> {
fn clone(&self) -> Self {
Self {
slot: self.slot.clone(),
}
}
}