use crate::effect::trait_def::Effect;
#[cfg(feature = "tracing")]
#[derive(Debug)]
pub struct Instrument<E> {
pub(crate) inner: E,
pub(crate) span: tracing::Span,
}
#[cfg(feature = "tracing")]
impl<E> Effect for Instrument<E>
where
E: Effect,
{
type Output = E::Output;
type Error = E::Error;
type Env = E::Env;
async fn run(self, env: &Self::Env) -> Result<Self::Output, Self::Error> {
use tracing::Instrument as _;
self.inner.run(env).instrument(self.span).await
}
}
#[cfg(feature = "tracing")]
pub trait EffectTracingExt: Effect {
fn instrument(self, span: tracing::Span) -> Instrument<Self> {
Instrument { inner: self, span }
}
}
#[cfg(feature = "tracing")]
impl<E: Effect> EffectTracingExt for E {}
#[cfg(all(test, feature = "tracing"))]
mod tests {
use super::*;
use crate::effect::compat::RunStandalone;
use crate::effect::constructors::{fail, pure};
use crate::effect::ext::EffectExt;
#[tokio::test]
async fn test_instrument_returns_value() {
let effect = pure::<_, String, ()>(42).instrument(tracing::info_span!("test_span"));
let result = effect.run_standalone().await;
assert_eq!(result, Ok(42));
}
#[tokio::test]
async fn test_error_in_span_propagates() {
let effect =
fail::<i32, _, ()>("oops".to_string()).instrument(tracing::info_span!("failing"));
let result = effect.run_standalone().await;
assert_eq!(result, Err("oops".to_string()));
}
#[tokio::test]
async fn test_nested_spans() {
let inner = pure::<_, String, ()>(1).instrument(tracing::debug_span!("inner_op"));
let outer = inner.and_then(|x| pure(x + 1).instrument(tracing::debug_span!("outer_op")));
let result = outer.run_standalone().await;
assert_eq!(result, Ok(2));
}
#[tokio::test]
async fn test_composition_with_instrument() {
let effect = pure::<_, String, ()>(5)
.instrument(tracing::debug_span!("step1"))
.map(|x| x * 2)
.instrument(tracing::debug_span!("step2"))
.and_then(|x| pure(x + 10).instrument(tracing::debug_span!("step3")));
let result = effect.run_standalone().await;
assert_eq!(result, Ok(20));
}
}