dptree/handler/
filter_map.rs1use crate::{
2 di::{Asyncify, Injectable},
3 from_fn_with_description, Handler, HandlerDescription, HandlerSignature, Type,
4};
5
6use std::{collections::BTreeSet, iter::FromIterator, ops::ControlFlow, sync::Arc};
7
8#[must_use]
15#[track_caller]
16pub fn filter_map<'a, Projection, Output, NewType, Args, Descr>(
17 proj: Projection,
18) -> Handler<'a, Output, Descr>
19where
20 Asyncify<Projection>: Injectable<Option<NewType>, Args> + Send + Sync + 'a,
21 Output: 'a,
22 Descr: HandlerDescription,
23 NewType: Send + Sync + 'static,
24{
25 filter_map_with_description(Descr::filter_map(), proj)
26}
27
28#[must_use]
30#[track_caller]
31pub fn filter_map_async<'a, Projection, Output, NewType, Args, Descr>(
32 proj: Projection,
33) -> Handler<'a, Output, Descr>
34where
35 Projection: Injectable<Option<NewType>, Args> + Send + Sync + 'a,
36 Output: 'a,
37 Descr: HandlerDescription,
38 NewType: Send + Sync + 'static,
39{
40 filter_map_async_with_description(Descr::filter_map_async(), proj)
41}
42
43#[must_use]
45#[track_caller]
46pub fn filter_map_with_description<'a, Projection, Output, NewType, Args, Descr>(
47 description: Descr,
48 proj: Projection,
49) -> Handler<'a, Output, Descr>
50where
51 Asyncify<Projection>: Injectable<Option<NewType>, Args> + Send + Sync + 'a,
52 Output: 'a,
53 NewType: Send + Sync + 'static,
54{
55 filter_map_async_with_description(description, Asyncify(proj))
56}
57
58#[must_use]
60#[track_caller]
61pub fn filter_map_async_with_description<'a, Projection, Output, NewType, Args, Descr>(
62 description: Descr,
63 proj: Projection,
64) -> Handler<'a, Output, Descr>
65where
66 Projection: Injectable<Option<NewType>, Args> + Send + Sync + 'a,
67 Output: 'a,
68 NewType: Send + Sync + 'static,
69{
70 let proj = Arc::new(proj);
71
72 from_fn_with_description(
73 description,
74 move |container, cont| {
75 let proj = Arc::clone(&proj);
76
77 async move {
78 let proj = proj.inject(&container);
79 let res = proj().await;
80 std::mem::drop(proj);
81
82 match res {
83 Some(new_type) => {
84 let mut intermediate = container.clone();
85 intermediate.insert(new_type);
86 match cont(intermediate).await {
87 ControlFlow::Continue(_) => ControlFlow::Continue(container),
88 ControlFlow::Break(result) => ControlFlow::Break(result),
89 }
90 }
91 None => ControlFlow::Continue(container),
92 }
93 }
94 },
95 HandlerSignature::Other {
96 obligations: Projection::obligations(),
97 guaranteed_outcomes: BTreeSet::from_iter(vec![Type::of::<NewType>()]),
98 conditional_outcomes: BTreeSet::default(),
99 continues: true,
100 },
101 )
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use crate::{deps, help_inference};
108
109 #[tokio::test]
110 async fn test_some() {
111 let value = 123;
112
113 let result = help_inference(filter_map(move || Some(value)))
114 .endpoint(move |event: i32| async move {
115 assert_eq!(event, value);
116 value
117 })
118 .dispatch(deps![])
119 .await;
120
121 assert!(result == ControlFlow::Break(value));
122 }
123
124 #[tokio::test]
125 async fn test_none() {
126 let result = help_inference(filter_map(|| None::<i32>))
127 .endpoint(|| async move { unreachable!() })
128 .dispatch(deps![])
129 .await;
130
131 assert!(result == ControlFlow::Continue(crate::deps![]));
132 }
133}