rusty_junctions_client_api_macro/lib.rs
1#![deny(missing_docs)]
2
3//! Crate providing a number of [Declarative
4//! Macros](https://doc.rust-lang.org/reference/macros-by-example.html)
5//! that can be used as part of the Public API of [Rusty
6//! Junctions](https://crates.io/crates/rusty_junctions), to define the
7//! various
8//! [Junction](https://docs.rs/rusty_junctions/0.1.0/rusty_junctions/struct.Junction.html),
9//! [Channels](https://docs.rs/rusty_junctions/0.1.0/rusty_junctions/channels/index.html),
10//! and [Join
11//! Patterns](https://docs.rs/rusty_junctions/0.1.0/rusty_junctions/patterns/index.html)
12//! that are used to declaratively define the desired concurrent computation.
13
14/// An alternate syntax for defining Join Pattern channel constrains.
15///
16/// The [`when`](when) macro allows for a number of channels to be defined
17/// using a list like syntax, opposed to the standard method chaining
18/// provided by the default Public API of [Rusty
19/// Junctions](https://crates.io/crates/rusty_junctions).
20///
21/// # Example
22/// ## Standard API
23/// By using the standard Public API we can define our computation using the following syntax.
24/// ```rust
25///
26/// let junction = rusty_junctions::Junction::new();
27/// let chan1 = junction.send_channel::<i32>();
28/// let chan2 = junction.send_channel::<i32>();
29/// let chan3 = junction.send_channel::<i32>();
30///
31/// junction.when(&chan1)
32/// .and(&chan2)
33/// .and(&chan3)
34/// .then_do(|v1, v2, v3| println!("{v1}, {v2}, {v3}"))
35/// ```
36///
37/// ## `when` Macro API
38/// However, it is also possible to use this richer more expressive syntax, when creating the firing conditions for a Join Pattern.
39/// ```rust
40/// let junction = rusty_junctions::Junction::new();
41/// let chan1 = junction.send_channel::<i32>();
42/// let chan2 = junction.send_channel::<i32>();
43/// let chan3 = junction.send_channel::<i32>();
44///
45/// when!(junction; chan1, chan2, chan3)
46/// .then_do(|v1, v2, v3| println!("{v1}, {v2}, {v3}"))
47/// ```
48#[macro_export]
49macro_rules! when {
50 ( $junction:ident; $initial_channel:ident, $( $other_channels:ident ),* ) => {
51 $junction.when(&$initial_channel)$( .and(&$other_channels) )*
52 }
53}
54
55/// An internal macro used by the [`junction`](junction) macro.
56///
57/// The [`channel`](channel) macro is used internally by the
58/// [`junction`](junction) macro. For this reason it needs to be
59/// made available as part of the Public API of the [Rusty
60/// Junctions](https://crates.io/crates/rusty_junctions) crate.
61#[macro_export]
62macro_rules! channel {
63 (Send, $junction:ident, $name:ident, $type:ty) => {
64 let $name = $junction.send_channel::<$type>();
65 };
66 (Recv, $junction:ident, $name:ident, $type:ty) => {
67 let $name = $junction.recv_channel::<$type>();
68 };
69 (Bidir, $junction:ident, $name:ident, $type:ty) => {
70 let $name = $junction.bidir_channel::<$type>();
71 };
72}
73
74/// Define an entire
75/// [`Junction`](https://docs.rs/rusty_junctions/0.1.0/rusty_junctions/struct.Junction.html)
76/// using a single block.
77///
78/// A
79/// [`Junction`](https://docs.rs/rusty_junctions/0.1.0/rusty_junctions/struct.Junction.html)
80/// is the smallest unit of distribution for the Join Calculus. This
81/// syntax allows for an entire junction to be defined as a single block.
82///
83/// # Example
84/// ## Standard API
85/// Suppose we use the standard Public API we can define our computation using the following syntax.
86/// ```rust
87/// let junction = rusty_junctions::Junction::new();
88/// let name = junction.send_channel::<String>();
89/// let value = junction.send_channel::<i32>();
90/// junction.when(&name).and(&value).then_do(|name, value| {
91/// println!("Standard API: {name} {value}");
92/// });
93/// value.send(0).unwrap();
94/// name.send(String::from("Hello, World!")).unwrap();
95/// ```
96///
97/// ## Single Junction Declarative Macro API
98/// However, it is also possible to use this alternative (simpler?) syntax for defining the entire
99/// [`Junction`](https://docs.rs/rusty_junctions/0.1.0/rusty_junctions/struct.Junction.html).
100/// ```rust
101/// let (name, value, mut handle) = junction! {
102/// name as Send::String,
103/// value as Send::i32,
104/// |name, value| {
105/// println!("Single Junction Declarative Macro API: {name} {value}");
106/// },
107/// };
108/// value.send(1).unwrap();
109/// name.send(String::from("Hello, World!")).unwrap();
110/// // Explicitly stop the Controller
111///
112/// handle.stop();
113/// ```
114///
115/// # Advantages
116/// * The main advantage is that we have a convenient and less verbose
117/// syntax for defining the
118/// [`Junction`](https://docs.rs/rusty_junctions/0.1.0/rusty_junctions/struct.Junction.html).
119///
120/// * This syntax allows for the entire junction to be defined in a single
121/// block. Defining all of the intermediate values in a nested scope,
122/// preventing them from being accessed from elsewhere in the client.
123///
124/// # Disadvantages
125/// * When using the [`junction`](junction) macro you are required
126/// to bring the [`when`](when) and [`channel`](channel) into
127/// the name space as well. This is due to a limitation of [Declarative
128/// Macros](https://doc.rust-lang.org/reference/macros-by-example.html),
129/// that they are not composed as single units, and are instead executed
130/// in a function like manner albeit during compilation and not
131/// runtime. A future implementation might utilise a Incremental TT
132/// Muncher, and deeper recursion to avoid having calls to other macros
133/// ([`when`](when) and [`channel`](channel)) which then need to
134/// be included in the name space of the client.
135///
136/// * As you can see in the [Example](#Example) above the Controller
137/// explicitly stopped, if we allowed it to be dropped from the inner
138/// scope there would be no guarantee it would have time for the pattern
139/// to fire.
140///
141#[macro_export]
142macro_rules! junction {
143 ( $( $channel_name:ident as $mode:ident :: $type:ty),* $(,)+
144 $( | $( $args:ident ),* | $function_block:block ),* $(,)+ ) => {{
145 let mut j = rusty_junctions::Junction::new();
146
147 $( channel!($mode, j, $channel_name, $type); )*
148
149 $(
150 when!(j; $( $args ),* ).then_do(| $( $args ),* | $function_block );
151 )*
152
153 let controller_handle = j
154 .controller_handle()
155 .expect("Failed to get controller handle");
156
157 ( $( $channel_name ),* , controller_handle)
158 }};
159}