ferrite_session/internal/session/choice/external/
offer_choice.rs1use core::{
2 future::Future,
3 marker::PhantomData,
4 pin::Pin,
5};
6
7use tokio::task;
8
9use crate::internal::{
10 base::{
11 unsafe_create_session,
12 unsafe_run_session,
13 Context,
14 PartialSession,
15 Protocol,
16 ProviderEndpointF,
17 },
18 functional::{
19 App,
20 AppSum,
21 NaturalTransformation,
22 RowCon,
23 SumFunctor,
24 ToRow,
25 TyCon,
26 TypeApp,
27 },
28 protocol::ExternalChoice,
29 session::choice::run_cont::RunCont,
30};
31
32pub fn offer_choice<C, Row1, Row2>(
33 cont1: impl for<'r> FnOnce(
34 AppSum<'r, Row2, InjectSessionF<'r, Row1, C>>,
35 ) -> ContSum<'r, Row1, C>
36 + Send
37 + 'static
38) -> PartialSession<C, ExternalChoice<Row1>>
39where
40 C: Context,
41 Row1: Send + 'static,
42 Row2: Send + 'static,
43 Row1: ToRow<Row = Row2>,
44 Row2: RowCon,
45 Row2: SumFunctor,
46{
47 unsafe_create_session::<C, ExternalChoice<Row1>, _, _>(
48 move |ctx, choice_receiver| async move {
49 let provider_end_sum = choice_receiver.recv().await.unwrap();
50
51 let cont_sum_1 =
52 provider_end_sum_to_cont_sum::<C, Row1, Row2>(ctx, provider_end_sum);
53
54 let res = cont1(cont_sum_1);
55
56 task::spawn(async move {
57 res.future.await;
58 });
59 },
60 )
61}
62
63pub struct InjectSession<'r, Row, C: Context, A>
64{
65 ctx: C::Endpoints,
66 provider_end: App<'r, ProviderEndpointF, A>,
67 phantom: PhantomData<(Box<dyn Invariant<'r>>, Row)>,
68}
69
70pub struct InjectSessionF<'r, Row, C>(PhantomData<&'r (Row, C)>);
71
72pub struct ContSum<'r, Row, C>
73{
74 future: Pin<Box<dyn Future<Output = ()> + Send + 'r>>,
75 phantom: PhantomData<(Box<dyn Invariant<'r>>, C, Row)>,
76}
77
78impl<'r, Row: 'r, C: Context, A: Protocol> RunCont<C, A>
79 for InjectSession<'r, Row, C, A>
80where
81 Row: Send,
82{
83 type Ret = ContSum<'r, Row, C>;
84
85 fn run_cont(
86 self,
87 session: PartialSession<C, A>,
88 ) -> Self::Ret
89 {
90 ContSum {
91 future: Box::pin(async move {
92 unsafe_run_session(session, self.ctx, self.provider_end.get_applied())
93 .await;
94 }),
95 phantom: PhantomData,
96 }
97 }
98}
99
100pub trait Invariant<'r>: Send {}
101
102impl<'r, Row, C: 'static> TyCon for InjectSessionF<'r, Row, C> {}
103
104impl<'r, 'a, Row, C: Context, A: 'r> TypeApp<'a, A>
105 for InjectSessionF<'r, Row, C>
106where
107 Row: Send,
108{
109 type Applied = InjectSession<'r, Row, C, A>;
110}
111
112fn provider_end_sum_to_cont_sum<C, Row1: 'static, Row2: 'static>(
113 ctx: C::Endpoints,
114 provider_end_sum: AppSum<'static, Row2, ProviderEndpointF>,
115) -> AppSum<Row2, InjectSessionF<'static, Row1, C>>
116where
117 Row1: Send,
118 Row2: SumFunctor,
119 C: Context,
120{
121 struct ProviderEndToCont<C: Context>
122 {
123 ctx: C::Endpoints,
124 }
125
126 impl<'r, Row: Send, C: Context>
127 NaturalTransformation<'r, ProviderEndpointF, InjectSessionF<'r, Row, C>>
128 for ProviderEndToCont<C>
129 {
130 fn lift<A: 'r>(
131 self,
132 provider_end: App<'r, ProviderEndpointF, A>,
133 ) -> App<'r, InjectSessionF<'r, Row, C>, A>
134 {
135 App::new(InjectSession {
136 ctx: self.ctx,
137 provider_end,
138 phantom: PhantomData,
139 })
140 }
141 }
142
143 Row2::lift_sum(ProviderEndToCont { ctx }, provider_end_sum)
144}