use tracing::{event, instrument, Level};
use crate::core::context::{
downcast_context_data,
ContextDataExtractorImpl,
Handler, };
use crate::core::context_data::ContextData;
use crate::core::control::PipelineControl;
use crate::error::OrkaError;
use crate::pipeline::definition::Pipeline;
use std::future::Future;
use std::sync::Arc;
impl<TData, Err> Pipeline<TData, Err>
where
TData: 'static + Send + Sync,
Err: std::error::Error + From<OrkaError> + Send + Sync + 'static,
{
pub fn before_root<F>(
&mut self,
step_name: &str,
handler_fn: impl Fn(ContextData<TData>) -> F + Send + Sync + 'static,
) -> &mut Self
where
F: Future<Output = Result<PipelineControl, Err>> + Send + 'static,
{
self.ensure_step_exists(step_name);
let final_handler: Handler<TData, Err> = Box::new(move |ctx_data| Box::pin(handler_fn(ctx_data)));
self
.before
.entry(step_name.to_string())
.or_default()
.push(final_handler);
self
}
pub fn on_root<F>(
&mut self,
step_name: &str,
handler_fn: impl Fn(ContextData<TData>) -> F + Send + Sync + 'static,
) -> &mut Self
where
F: Future<Output = Result<PipelineControl, Err>> + Send + 'static,
{
self.ensure_step_exists(step_name);
let final_handler: Handler<TData, Err> = Box::new(move |ctx_data| Box::pin(handler_fn(ctx_data)));
self.on.entry(step_name.to_string()).or_default().push(final_handler);
self
}
pub fn after_root<F>(
&mut self,
step_name: &str,
handler_fn: impl Fn(ContextData<TData>) -> F + Send + Sync + 'static,
) -> &mut Self
where
F: Future<Output = Result<PipelineControl, Err>> + Send + 'static,
{
self.ensure_step_exists(step_name);
let final_handler: Handler<TData, Err> = Box::new(move |ctx_data| Box::pin(handler_fn(ctx_data)));
self.after.entry(step_name.to_string()).or_default().push(final_handler);
self
}
pub fn set_extractor<SData>(
&mut self,
step_name: &str,
extractor_fn: impl Fn(ContextData<TData>) -> Result<ContextData<SData>, OrkaError> + Send + Sync + 'static,
) -> &mut Self
where
SData: 'static + Send + Sync,
{
self.ensure_step_exists(step_name);
let extractor_impl = ContextDataExtractorImpl::<TData, SData>::new(extractor_fn);
self.extractors.insert(step_name.to_string(), Arc::new(extractor_impl));
event!(Level::DEBUG, %step_name, sub_context_data_type = %std::any::type_name::<SData>(), "Extractor set.");
self
}
pub fn set_extractor_with_merge<SData>(
&mut self,
step_name: &str,
extractor_fn: impl Fn(ContextData<TData>) -> Result<ContextData<SData>, OrkaError> + Send + Sync + 'static,
merge_fn: impl Fn(&mut TData, &SData) + Send + Sync + 'static,
) -> &mut Self
where
SData: 'static + Send + Sync,
{
self.ensure_step_exists(step_name);
let extractor_impl = ContextDataExtractorImpl::<TData, SData>::with_merge(extractor_fn, merge_fn);
self.extractors.insert(step_name.to_string(), Arc::new(extractor_impl));
event!(Level::DEBUG, %step_name, sub_context_data_type = %std::any::type_name::<SData>(), "Extractor with merge set.");
self
}
#[instrument(
name = "Pipeline::on<SData>",
skip_all,
fields(step_name, sub_context_data_type = %std::any::type_name::<SData>())
)]
pub fn on<SData, F>(
&mut self,
step_name: &str,
handler_fn: impl Fn(ContextData<SData>) -> F + Send + Sync + 'static,
) -> &mut Self
where
SData: 'static + Send + Sync, F: Future<Output = Result<PipelineControl, Err>> + Send + 'static,
{
self.ensure_step_exists(step_name);
let extractor_arc = self.extractors.get(step_name).cloned().unwrap_or_else(|| {
panic!(
"Orka setup error: No extractor found for step '{}' when registering on<{}> handler. Call set_extractor first.",
step_name,
std::any::type_name::<SData>()
)
});
let step_name_for_handler = step_name.to_string();
let user_sdata_handler_arc = Arc::new(handler_fn);
let wrapped_handler: Handler<TData, Err> = Box::new(move |root_ctx_data: ContextData<TData>| {
let current_extractor = extractor_arc.clone();
let user_sdata_handler = user_sdata_handler_arc.clone();
let step_name_clone = step_name_for_handler.clone();
Box::pin(async move {
event!(Level::TRACE, step_name = %step_name_clone, "Executing wrapped on<SData> handler. Attempting extraction.");
let any_sub_ctx_data = match current_extractor.extract_sub_context_data(root_ctx_data.clone()) {
Ok(boxed_any) => boxed_any,
Err(orka_extraction_err) => {
event!(Level::ERROR, step_name = %step_name_clone, error = %orka_extraction_err, "Extractor function failed.");
let final_err = match orka_extraction_err {
OrkaError::HandlerError { source } => OrkaError::ExtractorFailure {
step_name: step_name_clone.clone(),
source,
},
OrkaError::ExtractorFailure { source, step_name: _ } => OrkaError::ExtractorFailure {
step_name: step_name_clone.clone(),
source,
},
other_err => other_err,
};
return Err(Err::from(final_err)); }
};
let sub_sdata_ctx: ContextData<SData> = match downcast_context_data::<SData>(
any_sub_ctx_data,
current_extractor.sub_context_data_type_id(),
&step_name_clone,
) {
Ok(s_ctx_data) => s_ctx_data,
Err(orka_downcast_err) => {
event!(Level::ERROR, step_name = %step_name_clone, error = %orka_downcast_err, "Sub-context ContextData downcast failed.");
return Err(Err::from(orka_downcast_err)); }
};
event!(Level::TRACE, step_name = %step_name_clone, "Sub-context ContextData extraction and downcast successful.");
event!(Level::TRACE, step_name = %step_name_clone, "Calling user's on<SData> handler.");
let control = match (user_sdata_handler)(sub_sdata_ctx.clone()).await {
Ok(control) => control,
Err(handler_err) => {
event!(Level::ERROR, step_name = %step_name_clone, error = %handler_err, "User's on<SData> handler failed.");
return Err(handler_err);
}
};
if current_extractor.has_merge() {
event!(Level::TRACE, step_name = %step_name_clone, "Merging sub-context back into root context.");
if let Err(merge_err) = current_extractor.merge_sub_context_data(root_ctx_data, &sub_sdata_ctx) {
event!(Level::ERROR, step_name = %step_name_clone, error = %merge_err, "Merging sub-context back failed.");
return Err(Err::from(merge_err));
}
}
Ok(control)
})
});
self.on.entry(step_name.to_string()).or_default().push(wrapped_handler);
self.sub_handler_steps.insert(step_name.to_string());
event!(Level::DEBUG, "on<SData> handler registered.");
self
}
}