Skip to main content

ferrite_session/internal/session/choice/external/
offer_choice.rs

1use 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}