use std::collections::BTreeMap;
use std::fmt;
use std::sync::Arc;
use runx_contracts::{Receipt, Reference};
use runx_parser::GraphStep;
use crate::adapter::SkillOutput;
use super::{
EffectAdmission, EffectMetadataRefreshRequest, EffectOutputRequest, EffectReceiptRequest,
EffectReplay, EffectReplayOutputRequest, EffectReplayReceiptRequest, EffectStepRequest,
RuntimeEffect, RuntimeEffectError,
};
#[derive(Clone)]
pub struct RuntimeEffectRegistry {
families: BTreeMap<&'static str, Arc<dyn RuntimeEffect>>,
}
impl RuntimeEffectRegistry {
#[must_use]
pub fn empty() -> Self {
Self {
families: BTreeMap::new(),
}
}
#[must_use]
pub fn with_effect<T>(effect: T) -> Self
where
T: RuntimeEffect + 'static,
{
let mut registry = Self::empty();
let family = effect.family();
registry.families.insert(family, Arc::new(effect));
registry
}
pub fn register_effect<T>(&mut self, effect: T) -> Result<(), RuntimeEffectError>
where
T: RuntimeEffect + 'static,
{
let family = effect.family();
if self.families.contains_key(family) {
return Err(RuntimeEffectError::DuplicateFamily {
family: family.to_owned(),
});
}
self.families.insert(family, Arc::new(effect));
Ok(())
}
pub(crate) fn find_replay(
&self,
request: EffectStepRequest<'_>,
) -> Result<Option<EffectReplay>, RuntimeEffectError> {
for effect in self.families.values() {
if let Some(replay) = effect.find_replay(request)? {
return Ok(Some(replay));
}
}
Ok(None)
}
pub(crate) fn recover_pending(
&self,
request: EffectStepRequest<'_>,
) -> Result<(), RuntimeEffectError> {
for effect in self.families.values() {
effect.recover_pending(request)?;
}
Ok(())
}
pub(crate) fn admit(
&self,
request: EffectStepRequest<'_>,
) -> Result<Option<EffectAdmission>, RuntimeEffectError> {
for effect in self.families.values() {
if let Some(admission) = effect.admit(request)? {
return Ok(Some(admission));
}
}
Ok(None)
}
pub(crate) fn prepare_output(
&self,
request: EffectOutputRequest<'_>,
) -> Result<(), RuntimeEffectError> {
let family = request.admission.family();
self.require_effect(family)?.prepare_output(request)
}
pub(crate) fn finalize_output(
&self,
request: EffectReceiptRequest<'_>,
) -> Result<(), RuntimeEffectError> {
let family = request.admission.family();
self.require_effect(family)?.finalize_output(request)
}
pub(crate) fn persist(
&self,
request: EffectReceiptRequest<'_>,
) -> Result<(), RuntimeEffectError> {
let family = request.admission.family();
self.require_effect(family)?.persist(request)
}
pub(crate) fn prepare_replay_output(
&self,
request: EffectReplayOutputRequest<'_>,
) -> Result<(), RuntimeEffectError> {
let family = request.replay.family();
self.require_effect(family)?.prepare_replay_output(request)
}
pub(crate) fn validate_replay(
&self,
request: EffectReplayReceiptRequest<'_>,
) -> Result<(), RuntimeEffectError> {
let family = request.replay.family();
self.require_effect(family)?.validate_replay(request)
}
pub(crate) fn refresh_output_metadata(
&self,
output: &mut SkillOutput,
receipt: &Receipt,
) -> Result<(), RuntimeEffectError> {
for effect in self.families.values() {
effect.refresh_output_metadata(EffectMetadataRefreshRequest { output, receipt })?;
}
Ok(())
}
pub(crate) fn authority_grant_refs(
&self,
admission: &EffectAdmission,
) -> Result<Vec<Reference>, RuntimeEffectError> {
self.require_effect(admission.family())?
.authority_grant_refs(admission)
}
pub(crate) fn replay_authority_grant_refs(
&self,
replay: &EffectReplay,
) -> Result<Vec<Reference>, RuntimeEffectError> {
self.require_effect(replay.family())?
.replay_authority_grant_refs(replay)
}
pub(crate) fn allows_parallel_step(&self, step: &GraphStep) -> bool {
self.families
.values()
.all(|effect| effect.can_run_parallel(step))
}
fn require_effect(
&self,
family: &'static str,
) -> Result<&dyn RuntimeEffect, RuntimeEffectError> {
self.families.get(family).map(Arc::as_ref).ok_or_else(|| {
RuntimeEffectError::MissingFamily {
family: family.to_owned(),
}
})
}
}
impl Default for RuntimeEffectRegistry {
fn default() -> Self {
Self::empty()
}
}
impl fmt::Debug for RuntimeEffectRegistry {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let families = self.families.keys().copied().collect::<Vec<_>>();
formatter
.debug_struct("RuntimeEffectRegistry")
.field("families", &families)
.finish()
}
}