1use tokio::{
2 task,
3 try_join,
4};
5
6use crate::internal::{
7 base::{
8 unsafe_create_session,
9 unsafe_run_session,
10 AppendContext,
11 Context,
12 Empty,
13 PartialSession,
14 Protocol,
15 Slot,
16 },
17 functional::{
18 App,
19 Nat,
20 },
21};
22
23pub enum L {}
24
25pub enum R {}
26
27pub enum AllLeft {}
28
29pub enum AllRight {}
30
31pub trait SplitContext<C>
32where
33 C: Context,
34{
35 type Left: Context;
36
37 type Right: Context;
38
39 fn split_endpoints(
40 ctx: C::Endpoints
41 ) -> (
42 <Self::Left as Context>::Endpoints,
43 <Self::Right as Context>::Endpoints,
44 );
45}
46
47impl SplitContext<()> for ()
48{
49 type Left = ();
50 type Right = ();
51
52 fn split_endpoints(_: ()) -> ((), ())
53 {
54 ((), ())
55 }
56}
57
58impl<C> SplitContext<C> for AllLeft
59where
60 C: Context,
61{
62 type Left = C;
63 type Right = ();
64
65 fn split_endpoints(ctx: C::Endpoints) -> (C::Endpoints, ())
66 {
67 (ctx, ())
68 }
69}
70
71impl<C> SplitContext<C> for AllRight
72where
73 C: Context,
74{
75 type Left = ();
76 type Right = C;
77
78 fn split_endpoints(ctx: C::Endpoints) -> ((), C::Endpoints)
79 {
80 ((), ctx)
81 }
82}
83
84impl<X, C, C1, C2> SplitContext<(Empty, C)> for (Empty, X)
85where
86 C: Context,
87 C1: Context,
88 C2: Context,
89 X: SplitContext<C, Left = C1, Right = C2>,
90{
91 type Left = (Empty, C1);
92 type Right = (Empty, C2);
93
94 fn split_endpoints(
95 (a, ctx): ((), C::Endpoints)
96 ) -> (((), C1::Endpoints), ((), C2::Endpoints))
97 {
98 let (ctx1, ctx2) = X::split_endpoints(ctx);
99
100 ((a, ctx1), ((), ctx2))
101 }
102}
103
104impl<X, A, C, C1, C2> SplitContext<(A, C)> for (L, X)
105where
106 A: Slot,
107 C: Context,
108 C1: Context,
109 C2: Context,
110 X: SplitContext<C, Left = C1, Right = C2>,
111{
112 type Left = (A, C1);
113 type Right = (Empty, C2);
114
115 fn split_endpoints(
116 (a, ctx): (A::Endpoint, C::Endpoints)
117 ) -> ((A::Endpoint, C1::Endpoints), ((), C2::Endpoints))
118 {
119 let (ctx1, ctx2) = X::split_endpoints(ctx);
120
121 ((a, ctx1), ((), ctx2))
122 }
123}
124
125impl<X, A, C, C1, C2> SplitContext<(A, C)> for (R, X)
126where
127 A: Slot,
128 C: Context,
129 C1: Context,
130 C2: Context,
131 X: SplitContext<C, Left = C1, Right = C2>,
132{
133 type Left = (Empty, C1);
134 type Right = (A, C2);
135
136 fn split_endpoints(
137 (a, ctx): (A::Endpoint, C::Endpoints)
138 ) -> (((), C1::Endpoints), (A::Endpoint, C2::Endpoints))
139 {
140 let (ctx1, ctx2) = X::split_endpoints(ctx);
141
142 (((), ctx1), (a, ctx2))
143 }
144}
145
146pub trait Cut<C>: SplitContext<C>
147where
148 C: Context,
149{
150 fn cut<A, B>(
151 cont1: PartialSession<Self::Left, A>,
152 cont2: impl FnOnce(
153 <Self::Right as Context>::Length,
154 ) -> PartialSession<
155 <Self::Right as AppendContext<(A, ())>>::Appended,
156 B,
157 >,
158 ) -> PartialSession<C, B>
159 where
160 A: Protocol,
161 B: Protocol,
162 Self::Right: AppendContext<(A, ())>;
163}
164
165impl<X, C> Cut<C> for X
166where
167 C: Context,
168 X: SplitContext<C>,
169{
170 fn cut<A, B>(
171 cont1: PartialSession<Self::Left, A>,
172 cont2: impl FnOnce(
173 <Self::Right as Context>::Length,
174 ) -> PartialSession<
175 <Self::Right as AppendContext<(A, ())>>::Appended,
176 B,
177 >,
178 ) -> PartialSession<C, B>
179 where
180 A: Protocol,
181 B: Protocol,
182 Self::Right: AppendContext<(A, ())>,
183 {
184 cut::<X, _, _, _, _, _, _>(cont1, cont2)
185 }
186}
187
188pub fn cut<X, C, C1, C2, A, B, Func>(
189 cont1: PartialSession<C1, A>,
190 cont2: Func,
191) -> PartialSession<C, B>
192where
193 A: Protocol,
194 B: Protocol,
195 C: Context,
196 C1: Context,
197 C2: Context,
198 X: SplitContext<C, Left = C1, Right = C2>,
199 C2: AppendContext<(A, ())>,
200 Func: FnOnce(C2::Length) -> PartialSession<C2::Appended, B>,
201{
202 let cont3 = cont2(C2::Length::nat());
203
204 unsafe_create_session(move |ctx, sender1| async move {
205 let (ctx1, ctx2) = X::split_endpoints(ctx);
206
207 let (provider_end_a, client_end_a) = A::create_endpoints();
208
209 let ctx3 = C2::append_context(ctx2, (App::new(client_end_a), ()));
210
211 let child1 = task::spawn(async move {
212 unsafe_run_session(cont3, ctx3, sender1).await;
213 });
214
215 let child2 = task::spawn(async {
216 unsafe_run_session(cont1, ctx1, provider_end_a).await;
217 });
218
219 try_join!(child1, child2).unwrap();
220 })
221}
222
223pub fn cut_append<C1, C2, C3, C4, A, B>(
232 cont1: PartialSession<C3, B>,
233 cont2: PartialSession<C2, A>,
234) -> PartialSession<C4, B>
235where
236 A: Protocol,
237 B: Protocol,
238 C1: Context,
239 C2: Context,
240 C3: Context,
241 C4: Context,
242 C1: AppendContext<(A, ()), Appended = C3>,
243 C1: AppendContext<C2, Appended = C4>,
244{
245 unsafe_create_session(move |ctx1, b_sender| async move {
246 let (ctx2, ctx3) = <C1 as AppendContext<C2>>::split_context(ctx1);
247
248 let (provider_end_a, client_end_a) = A::create_endpoints();
249
250 let ctx4 = <C1 as AppendContext<(A, ())>>::append_context(
251 ctx2,
252 (App::new(client_end_a), ()),
253 );
254
255 let child1 = task::spawn(async {
256 unsafe_run_session(cont1, ctx4, b_sender).await;
257 });
258
259 let child2 = task::spawn(async {
260 unsafe_run_session(cont2, ctx3, provider_end_a).await;
261 });
262
263 try_join!(child1, child2).unwrap();
264 })
265}