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}