1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use std::collections::LinkedList;

use crate::internal::{
  base::{
    AppendContext,
    Context,
    ContextLens,
    Empty,
    PartialSession,
    Protocol,
    Session,
  },
  protocol::End,
  session::{
    context::append_emtpy_slot,
    cut::{
      AllRight,
      Cut,
    },
    end::{
      terminate,
      wait,
    },
  },
};

pub fn include_session<C1, C2, N, A, B>(
  session: Session<A>,
  cont: impl FnOnce(N) -> PartialSession<C2, B>,
) -> PartialSession<C1, B>
where
  A: Protocol,
  B: Protocol,
  C1: Context<Length = N>,
  C2: Context,
  C1: AppendContext<(A, ()), Appended = C2>,
{
  AllRight::cut(session, cont)
}

pub fn wait_session<I, P>(
  session1: Session<End>,
  cont: PartialSession<I, P>,
) -> PartialSession<I, P>
where
  P: Protocol,
  I: Context,
  I: AppendContext<(End, ())>,
  I: AppendContext<(Empty, ())>,
  I::Length: ContextLens<
    <I as AppendContext<(End, ())>>::Appended,
    End,
    Empty,
    Target = <I as AppendContext<(Empty, ())>>::Appended,
  >,
{
  include_session(session1, move |chan| wait(chan, append_emtpy_slot(cont)))
}

pub fn wait_sessions<I, P>(
  sessions: Vec<Session<End>>,
  cont: PartialSession<I, P>,
) -> PartialSession<I, P>
where
  P: Protocol,
  I: AppendContext<(End, ())>,
  I: AppendContext<(Empty, ())>,
  I::Length: ContextLens<
    <I as AppendContext<(End, ())>>::Appended,
    End,
    Empty,
    Target = <I as AppendContext<(Empty, ())>>::Appended,
  >,
{
  wait_session(join_sessions(sessions), cont)
}

pub fn join_sessions(sessions: Vec<Session<End>>) -> Session<End>
{
  do_join_sessions(sessions.into_iter().collect())
}

fn do_join_sessions(mut sessions: LinkedList<Session<End>>) -> Session<End>
{
  match sessions.pop_front() {
    Some(session) => include_session(session, move |c1| {
      include_session(do_join_sessions(sessions), move |c2| {
        wait(c1, wait(c2, terminate()))
      })
    }),
    None => terminate(),
  }
}