Skip to main content

flowly_service/
switch.rs

1use std::marker::PhantomData;
2
3use futures::{Stream, StreamExt};
4
5use crate::{Context, Service, stub::Stub};
6
7pub trait SwitchCaseMatch<T: ?Sized + PartialEq> {
8    fn check_match(&self, needle: &T) -> bool;
9}
10
11impl<const N: usize, T: ?Sized + PartialEq> SwitchCaseMatch<T> for [&T; N] {
12    fn check_match(&self, needle: &T) -> bool {
13        self.contains(&needle)
14    }
15}
16
17impl<const N: usize, T: ?Sized + PartialEq> SwitchCaseMatch<T> for &[&T; N] {
18    fn check_match(&self, needle: &T) -> bool {
19        self.contains(&needle)
20    }
21}
22
23impl<const N: usize, T: PartialEq> SwitchCaseMatch<T> for [T; N] {
24    fn check_match(&self, needle: &T) -> bool {
25        self.contains(needle)
26    }
27}
28
29impl<const N: usize, T: PartialEq> SwitchCaseMatch<T> for &[T; N] {
30    fn check_match(&self, needle: &T) -> bool {
31        self.contains(needle)
32    }
33}
34
35impl<T: PartialEq> SwitchCaseMatch<T> for T {
36    fn check_match(&self, needle: &T) -> bool {
37        self == needle
38    }
39}
40
41impl<T: ?Sized + PartialEq> SwitchCaseMatch<T> for &T {
42    fn check_match(&self, needle: &T) -> bool {
43        self == &needle
44    }
45}
46
47impl<T: ?Sized + PartialEq> SwitchCaseMatch<T> for &[&T] {
48    fn check_match(&self, needle: &T) -> bool {
49        self.contains(&needle)
50    }
51}
52
53impl<T: PartialEq> SwitchCaseMatch<T> for &[T] {
54    fn check_match(&self, needle: &T) -> bool {
55        self.contains(needle)
56    }
57}
58
59pub trait SwitchCase<I>: Service<I> {
60    fn try_match(&self, item: &I) -> bool;
61}
62
63impl<I: Send, O: Send> SwitchCase<I> for Stub<O> {
64    fn try_match(&self, _item: &I) -> bool {
65        false
66    }
67}
68
69#[derive(Clone)]
70pub struct SwitchDefaultCase<I, S, C> {
71    case: Option<C>,
72    service: S,
73    m: PhantomData<I>,
74}
75
76impl<I, S, C> Service<I> for SwitchDefaultCase<I, S, C>
77where
78    S: Service<I>,
79    C: SwitchCase<I, Out = S::Out>,
80    S::Out: Send,
81{
82    type Out = S::Out;
83
84    #[allow(clippy::collapsible_if)]
85    fn handle(&mut self, input: I, cx: &Context) -> impl Stream<Item = Self::Out> + Send {
86        if let Some(case) = &mut self.case {
87            if case.try_match(&input) {
88                return case.handle(input, cx).left_stream();
89            }
90        }
91
92        self.service.handle(input, cx).right_stream()
93    }
94}
95
96#[derive(Clone)]
97pub struct SwitchMatchCase<I, F, A, S, C> {
98    case: Option<C>,
99    selector: F,
100    service: S,
101    variant: A,
102    m: PhantomData<I>,
103}
104
105impl<I, F, A, S, C> Service<I> for SwitchMatchCase<I, F, A, S, C>
106where
107    S: Service<I>,
108    C: SwitchCase<I, Out = S::Out>,
109    S::Out: Send,
110{
111    type Out = S::Out;
112
113    #[allow(clippy::collapsible_if)]
114    fn handle(&mut self, input: I, cx: &Context) -> impl Stream<Item = Self::Out> + Send {
115        if let Some(case) = &mut self.case {
116            if case.try_match(&input) {
117                return case.handle(input, cx).left_stream();
118            }
119        }
120
121        self.service.handle(input, cx).right_stream()
122    }
123}
124
125impl<I, F, D, A, S, C> SwitchCase<I> for SwitchMatchCase<I, F, A, S, C>
126where
127    S: Service<I>,
128    C: SwitchCase<I, Out = S::Out>,
129    A: SwitchCaseMatch<D>,
130    F: Fn(&I) -> D,
131    S::Out: Send,
132    D: std::cmp::PartialEq,
133{
134    #[allow(clippy::collapsible_if)]
135    fn try_match(&self, item: &I) -> bool {
136        if let Some(case) = &self.case {
137            if case.try_match(item) {
138                return true;
139            }
140        }
141
142        let d = (self.selector)(item);
143
144        self.variant.check_match(&d)
145    }
146}
147
148impl<I, F, D, A, S, C> SwitchMatchCase<I, F, A, S, C>
149where
150    I: Send,
151    S: Service<I>,
152    S::Out: Send,
153    F: Clone + Fn(&I) -> D,
154    C: SwitchCase<I, Out = S::Out>,
155    A: SwitchCaseMatch<D>,
156    D: std::cmp::PartialEq,
157{
158    #[inline]
159    pub fn case<B, Cs>(
160        self,
161        variant: B,
162        service: Cs,
163    ) -> SwitchMatchCase<I, F, B, Cs, impl SwitchCase<I, Out = S::Out>>
164    where
165        A: SwitchCaseMatch<D>,
166        Cs: Service<I, Out = S::Out>,
167    {
168        SwitchMatchCase {
169            selector: self.selector.clone(),
170            case: Some(self),
171            variant,
172            service,
173            m: PhantomData,
174        }
175    }
176
177    #[inline]
178    pub fn default<Cs>(
179        self,
180        service: Cs,
181    ) -> SwitchDefaultCase<I, Cs, impl SwitchCase<I, Out = S::Out>>
182    where
183        Cs: Service<I, Out = S::Out>,
184    {
185        SwitchDefaultCase {
186            case: Some(self),
187            service,
188            m: PhantomData,
189        }
190    }
191}
192
193#[derive(Clone)]
194pub struct Switch<I, O, F, D> {
195    selector: F,
196    m: PhantomData<(I, O, D)>,
197}
198
199impl<I, O, F, D> Switch<I, O, F, D>
200where
201    I: Send,
202    D: PartialEq,
203    O: Send,
204    F: Copy + Fn(&I) -> D,
205{
206    #[inline]
207    pub fn case<A, Cs>(
208        self,
209        variant: A,
210        service: Cs,
211    ) -> SwitchMatchCase<I, F, A, Cs, impl SwitchCase<I, Out = O>>
212    where
213        A: SwitchCaseMatch<D>,
214        Cs: Service<I, Out = O>,
215    {
216        SwitchMatchCase {
217            case: None::<Stub<O>>,
218            selector: self.selector,
219            variant,
220            service,
221            m: PhantomData,
222        }
223    }
224
225    #[inline]
226    pub fn default<Cs>(self, service: Cs) -> SwitchDefaultCase<I, Cs, impl SwitchCase<I, Out = O>>
227    where
228        Cs: Service<I, Out = O>,
229    {
230        SwitchDefaultCase {
231            case: None::<Stub<O>>,
232            service,
233            m: PhantomData,
234        }
235    }
236}
237
238pub fn switch<I, O, F, D>(selector: F) -> Switch<I, O, F, D>
239where
240    F: Fn(&I) -> D,
241{
242    Switch {
243        selector,
244        m: PhantomData,
245    }
246}