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
69pub struct SwitchDefaultCase<I, S, C> {
70    case: Option<C>,
71    service: S,
72    m: PhantomData<I>,
73}
74
75impl<I, S, C> Service<I> for SwitchDefaultCase<I, S, C>
76where
77    S: Service<I>,
78    C: SwitchCase<I, Out = S::Out>,
79    S::Out: Send,
80{
81    type Out = S::Out;
82
83    fn handle(&mut self, input: I, cx: &Context) -> impl Stream<Item = Self::Out> + Send {
84        if let Some(case) = &mut self.case
85            && case.try_match(&input)
86        {
87            case.handle(input, cx).left_stream()
88        } else {
89            self.service.handle(input, cx).right_stream()
90        }
91    }
92}
93
94pub struct SwitchMatchCase<I, F, A, S, C> {
95    case: Option<C>,
96    selector: F,
97    service: S,
98    variant: A,
99    m: PhantomData<I>,
100}
101
102impl<I, F, A, S, C> Service<I> for SwitchMatchCase<I, F, A, S, C>
103where
104    S: Service<I>,
105    C: SwitchCase<I, Out = S::Out>,
106    S::Out: Send,
107{
108    type Out = S::Out;
109
110    fn handle(&mut self, input: I, cx: &Context) -> impl Stream<Item = Self::Out> + Send {
111        if let Some(case) = &mut self.case
112            && case.try_match(&input)
113        {
114            case.handle(input, cx).left_stream()
115        } else {
116            self.service.handle(input, cx).right_stream()
117        }
118    }
119}
120
121impl<I, F, D, A, S, C> SwitchCase<I> for SwitchMatchCase<I, F, A, S, C>
122where
123    S: Service<I>,
124    C: SwitchCase<I, Out = S::Out>,
125    A: SwitchCaseMatch<D>,
126    F: Fn(&I) -> D,
127    S::Out: Send,
128    D: std::cmp::PartialEq,
129{
130    fn try_match(&self, item: &I) -> bool {
131        if let Some(case) = &self.case
132            && case.try_match(item)
133        {
134            return true;
135        }
136
137        let d = (self.selector)(item);
138
139        self.variant.check_match(&d)
140    }
141}
142
143impl<I, F, D, A, S, C> SwitchMatchCase<I, F, A, S, C>
144where
145    I: Send,
146    S: Service<I>,
147    S::Out: Send,
148    F: Clone + Fn(&I) -> D,
149    C: SwitchCase<I, Out = S::Out>,
150    A: SwitchCaseMatch<D>,
151    D: std::cmp::PartialEq,
152{
153    #[inline]
154    pub fn case<B, Cs>(
155        self,
156        variant: B,
157        service: Cs,
158    ) -> SwitchMatchCase<I, F, B, Cs, impl SwitchCase<I, Out = S::Out>>
159    where
160        A: SwitchCaseMatch<D>,
161        Cs: Service<I, Out = S::Out>,
162    {
163        SwitchMatchCase {
164            selector: self.selector.clone(),
165            case: Some(self),
166            variant,
167            service,
168            m: PhantomData,
169        }
170    }
171
172    #[inline]
173    pub fn default<Cs>(
174        self,
175        service: Cs,
176    ) -> SwitchDefaultCase<I, Cs, impl SwitchCase<I, Out = S::Out>>
177    where
178        Cs: Service<I, Out = S::Out>,
179    {
180        SwitchDefaultCase {
181            case: Some(self),
182            service,
183            m: PhantomData,
184        }
185    }
186}
187
188pub struct Switch<I, O, F, D> {
189    selector: F,
190    m: PhantomData<(I, O, D)>,
191}
192
193impl<I, O, F, D> Switch<I, O, F, D>
194where
195    I: Send,
196    D: PartialEq,
197    O: Send,
198    F: Copy + Fn(&I) -> D,
199{
200    #[inline]
201    pub fn case<A, Cs>(
202        self,
203        variant: A,
204        service: Cs,
205    ) -> SwitchMatchCase<I, F, A, Cs, impl SwitchCase<I, Out = O>>
206    where
207        A: SwitchCaseMatch<D>,
208        Cs: Service<I, Out = O>,
209    {
210        SwitchMatchCase {
211            case: None::<Stub<O>>,
212            selector: self.selector,
213            variant,
214            service,
215            m: PhantomData,
216        }
217    }
218
219    #[inline]
220    pub fn default<Cs>(self, service: Cs) -> SwitchDefaultCase<I, Cs, impl SwitchCase<I, Out = O>>
221    where
222        Cs: Service<I, Out = O>,
223    {
224        SwitchDefaultCase {
225            case: None::<Stub<O>>,
226            service,
227            m: PhantomData,
228        }
229    }
230}
231
232pub fn switch<I, O, F, D>(selector: F) -> Switch<I, O, F, D>
233where
234    F: Fn(&I) -> D,
235{
236    Switch {
237        selector,
238        m: PhantomData,
239    }
240}