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}