use std::sync::Arc;
use crate::adapter::WrappingDispenser;
use crate::adapter::{ExecutionError, OpDispenser, OpResult};
use crate::wrapper_registry::{WrapperName, WrapperRegistration, WrapperSubject};
pub const NAME: WrapperName = WrapperName::new("if");
fn triggers(s: WrapperSubject) -> bool {
let Some(template) = s.op() else {
return false;
};
template.condition.is_some()
}
fn describe_assignment(s: WrapperSubject) -> Option<String> {
let template = s.op()?;
template.condition.as_ref().map(|cond| {
let trimmed = crate::wrapper_registrations::trim_braces(cond);
format!("if: {trimmed}")
})
}
inventory::submit! {
WrapperRegistration {
name: NAME,
owned_fields: &["if"],
triggers,
requires_inner: &[],
forbids_outer: &[],
mutually_exclusive_with: &[],
describe_assignment,
levels: &[crate::wrapper_registry::WrapperLevel::Op],
}
}
pub struct ConditionalDispenser {
inner: Arc<dyn OpDispenser>,
condition_handle: crate::fixture::PullHandle,
metrics: Arc<crate::activity::ActivityMetrics>,
}
impl ConditionalDispenser {
pub fn wrap(
inner: Arc<dyn OpDispenser>,
condition_field: &str,
metrics: Arc<crate::activity::ActivityMetrics>,
fx: &mut crate::fixture::ScopeFixture,
) -> Result<Arc<dyn OpDispenser>, String> {
let condition_handle = fx
.register_pull(condition_field)
.map_err(|e| format!("conditional `if`: {e}"))?;
Ok(Arc::new(Self {
inner,
condition_handle,
metrics,
}))
}
}
impl WrappingDispenser for ConditionalDispenser {}
impl OpDispenser for ConditionalDispenser {
fn execute<'a>(
&'a self,
cycle: u64,
ctx: &'a crate::fixture::ExecCtx<'a>,
) -> std::pin::Pin<
Box<dyn std::future::Future<Output = Result<OpResult, ExecutionError>> + Send + 'a>,
> {
Box::pin(async move {
let value = ctx.pulls.get(self.condition_handle);
if !crate::wrappers::condition::is_truthy(value) {
self.metrics.skips_total.inc();
return Ok(OpResult::skipped());
}
self.inner.execute(cycle, ctx).await
})
}
fn inner_dispenser(&self) -> Option<&dyn OpDispenser> {
Some(self.inner.as_ref())
}
}