dptree/handler/
inspect.rs

1use crate::{
2    di::{Asyncify, Injectable},
3    from_fn_with_description, Handler, HandlerDescription, HandlerSignature,
4};
5
6use std::{collections::BTreeSet, sync::Arc};
7
8/// Constructs a handler that inspects current state. Like [`map`] but does not
9/// add return value of `f` to the container.
10///
11/// [`map`]: crate::map
12#[must_use]
13#[track_caller]
14pub fn inspect<'a, F, Output, Args, Descr>(f: F) -> Handler<'a, Output, Descr>
15where
16    Asyncify<F>: Injectable<(), Args> + Send + Sync + 'a,
17    Output: 'a,
18    Descr: HandlerDescription,
19{
20    inspect_with_description(Descr::inspect(), f)
21}
22
23/// The asynchronous version of [`inspect`].
24#[must_use]
25#[track_caller]
26pub fn inspect_async<'a, F, Output, Args, Descr>(f: F) -> Handler<'a, Output, Descr>
27where
28    F: Injectable<(), Args> + Send + Sync + 'a,
29    Output: 'a,
30    Descr: HandlerDescription,
31{
32    inspect_async_with_description(Descr::inspect_async(), f)
33}
34
35/// [`inspect`] with a custom description.
36#[must_use]
37#[track_caller]
38pub fn inspect_with_description<'a, F, Output, Args, Descr>(
39    description: Descr,
40    f: F,
41) -> Handler<'a, Output, Descr>
42where
43    Asyncify<F>: Injectable<(), Args> + Send + Sync + 'a,
44    Output: 'a,
45{
46    inspect_async_with_description(description, Asyncify(f))
47}
48
49/// [`inspect_async`] with a custom description.
50#[must_use]
51#[track_caller]
52pub fn inspect_async_with_description<'a, F, Output, Args, Descr>(
53    description: Descr,
54    f: F,
55) -> Handler<'a, Output, Descr>
56where
57    F: Injectable<(), Args> + Send + Sync + 'a,
58    Output: 'a,
59{
60    let f = Arc::new(f);
61
62    from_fn_with_description(
63        description,
64        move |x, cont| {
65            let f = Arc::clone(&f);
66            async move {
67                {
68                    let f = f.inject(&x);
69                    f().await;
70                }
71
72                cont(x).await
73            }
74        },
75        HandlerSignature::Other {
76            obligations: F::obligations(),
77            guaranteed_outcomes: BTreeSet::default(),
78            conditional_outcomes: BTreeSet::default(),
79        },
80    )
81}
82
83#[cfg(test)]
84mod tests {
85    use std::{
86        ops::ControlFlow,
87        sync::atomic::{AtomicBool, Ordering},
88    };
89
90    use super::*;
91    use crate::{deps, help_inference};
92
93    #[tokio::test]
94    async fn test_inspect() {
95        let value = 123;
96        let inspect_passed = Arc::new(AtomicBool::new(false));
97        let inspect_passed_cloned = Arc::clone(&inspect_passed);
98
99        let result: ControlFlow<(), _> = help_inference(inspect(move |x: i32| {
100            assert_eq!(x, value);
101            inspect_passed_cloned.swap(true, Ordering::Relaxed);
102        }))
103        .dispatch(deps![value])
104        .await;
105
106        assert!(matches!(result, ControlFlow::Continue(_)));
107        assert!(inspect_passed.load(Ordering::Relaxed));
108    }
109}