use crate::Worker;
use anyhow::bail;
use std::sync::{Arc, OnceLock};
use temporalio_common::protos::{
coresdk::{
workflow_activation::{WorkflowActivation, remove_from_cache::EvictionReason},
workflow_completion::WorkflowActivationCompletion,
},
temporal::api::common::v1::Payload,
};
#[async_trait::async_trait(?Send)]
pub trait WorkerInterceptor {
async fn on_workflow_activation_completion(&self, _completion: &WorkflowActivationCompletion) {}
fn on_shutdown(&self, _sdk_worker: &Worker) {}
async fn on_workflow_activation(
&self,
_activation: &WorkflowActivation,
) -> Result<(), anyhow::Error> {
Ok(())
}
}
pub struct InterceptorWithNext {
inner: Box<dyn WorkerInterceptor>,
next: Option<Box<InterceptorWithNext>>,
}
impl InterceptorWithNext {
pub fn new(inner: Box<dyn WorkerInterceptor>) -> Self {
Self { inner, next: None }
}
pub fn set_next(&mut self, next: Box<dyn WorkerInterceptor>) -> &mut InterceptorWithNext {
self.next.insert(Box::new(Self::new(next)))
}
}
#[async_trait::async_trait(?Send)]
impl WorkerInterceptor for InterceptorWithNext {
async fn on_workflow_activation_completion(&self, c: &WorkflowActivationCompletion) {
self.inner.on_workflow_activation_completion(c).await;
if let Some(next) = &self.next {
next.on_workflow_activation_completion(c).await;
}
}
fn on_shutdown(&self, w: &Worker) {
self.inner.on_shutdown(w);
if let Some(next) = &self.next {
next.on_shutdown(w);
}
}
async fn on_workflow_activation(&self, a: &WorkflowActivation) -> Result<(), anyhow::Error> {
self.inner.on_workflow_activation(a).await?;
if let Some(next) = &self.next {
next.on_workflow_activation(a).await?;
}
Ok(())
}
}
pub struct FailOnNondeterminismInterceptor {}
#[async_trait::async_trait(?Send)]
impl WorkerInterceptor for FailOnNondeterminismInterceptor {
async fn on_workflow_activation(
&self,
activation: &WorkflowActivation,
) -> Result<(), anyhow::Error> {
if matches!(
activation.eviction_reason(),
Some(EvictionReason::Nondeterminism)
) {
bail!("Workflow is being evicted because of nondeterminism! {activation}");
}
Ok(())
}
}
#[derive(Default)]
pub struct ReturnWorkflowExitValueInterceptor {
result_value: Arc<OnceLock<Payload>>,
}
impl ReturnWorkflowExitValueInterceptor {
pub fn result_handle(&self) -> Arc<OnceLock<Payload>> {
self.result_value.clone()
}
}
#[async_trait::async_trait(?Send)]
impl WorkerInterceptor for ReturnWorkflowExitValueInterceptor {
async fn on_workflow_activation_completion(&self, c: &WorkflowActivationCompletion) {
if let Some(v) = c.complete_workflow_execution_value() {
let _ = self.result_value.set(v.clone());
}
}
}