distant_net/manager/server/
handler.rs

1use std::future::Future;
2use std::io;
3
4use async_trait::async_trait;
5use distant_auth::Authenticator;
6
7use crate::client::UntypedClient;
8use crate::common::{Destination, Map};
9
10pub type BoxedLaunchHandler = Box<dyn LaunchHandler>;
11pub type BoxedConnectHandler = Box<dyn ConnectHandler>;
12
13/// Represents an interface to start a server at some remote `destination`.
14///
15/// * `destination` is the location where the server will be started.
16/// * `options` is provided to include extra information needed to launch or establish the
17///   connection.
18/// * `authenticator` is provided to support a challenge-based authentication while launching.
19///
20/// Returns a [`Destination`] representing the new origin to use if a connection is desired.
21#[async_trait]
22pub trait LaunchHandler: Send + Sync {
23    async fn launch(
24        &self,
25        destination: &Destination,
26        options: &Map,
27        authenticator: &mut dyn Authenticator,
28    ) -> io::Result<Destination>;
29}
30
31#[async_trait]
32impl<F, R> LaunchHandler for F
33where
34    F: Fn(&Destination, &Map, &mut dyn Authenticator) -> R + Send + Sync + 'static,
35    R: Future<Output = io::Result<Destination>> + Send + 'static,
36{
37    async fn launch(
38        &self,
39        destination: &Destination,
40        options: &Map,
41        authenticator: &mut dyn Authenticator,
42    ) -> io::Result<Destination> {
43        self(destination, options, authenticator).await
44    }
45}
46
47/// Generates a new [`LaunchHandler`] for the provided anonymous function in the form of
48///
49/// ```
50/// use distant_net::boxed_launch_handler;
51///
52/// let _handler = boxed_launch_handler!(|destination, options, authenticator| {
53///     todo!("Implement handler logic.");
54/// });
55///
56/// let _handler = boxed_launch_handler!(|destination, options, authenticator| async {
57///     todo!("We support async within as well regardless of the keyword!");
58/// });
59///
60/// let _handler = boxed_launch_handler!(move |destination, options, authenticator| {
61///     todo!("You can also explicitly mark to move into the closure");
62/// });
63/// ```
64#[macro_export]
65macro_rules! boxed_launch_handler {
66    (|$destination:ident, $options:ident, $authenticator:ident| $(async)? $body:block) => {{
67        let x: $crate::manager::BoxedLaunchHandler = Box::new(
68            |$destination: &$crate::common::Destination,
69             $options: &$crate::common::Map,
70             $authenticator: &mut dyn $crate::auth::Authenticator| async { $body },
71        );
72        x
73    }};
74    (move |$destination:ident, $options:ident, $authenticator:ident| $(async)? $body:block) => {{
75        let x: $crate::manager::BoxedLaunchHandler = Box::new(
76            move |$destination: &$crate::common::Destination,
77                  $options: &$crate::common::Map,
78                  $authenticator: &mut dyn $crate::auth::Authenticator| async move { $body },
79        );
80        x
81    }};
82}
83
84/// Represents an interface to perform a connection to some remote `destination`.
85///
86/// * `destination` is the location of the server to connect to.
87/// * `options` is provided to include extra information needed to establish the connection.
88/// * `authenticator` is provided to support a challenge-based authentication while connecting.
89///
90/// Returns an [`UntypedClient`] representing the connection.
91#[async_trait]
92pub trait ConnectHandler: Send + Sync {
93    async fn connect(
94        &self,
95        destination: &Destination,
96        options: &Map,
97        authenticator: &mut dyn Authenticator,
98    ) -> io::Result<UntypedClient>;
99}
100
101#[async_trait]
102impl<F, R> ConnectHandler for F
103where
104    F: Fn(&Destination, &Map, &mut dyn Authenticator) -> R + Send + Sync + 'static,
105    R: Future<Output = io::Result<UntypedClient>> + Send + 'static,
106{
107    async fn connect(
108        &self,
109        destination: &Destination,
110        options: &Map,
111        authenticator: &mut dyn Authenticator,
112    ) -> io::Result<UntypedClient> {
113        self(destination, options, authenticator).await
114    }
115}
116
117/// Generates a new [`ConnectHandler`] for the provided anonymous function in the form of
118///
119/// ```
120/// use distant_net::boxed_connect_handler;
121///
122/// let _handler = boxed_connect_handler!(|destination, options, authenticator| {
123///     todo!("Implement handler logic.");
124/// });
125///
126/// let _handler = boxed_connect_handler!(|destination, options, authenticator| async {
127///     todo!("We support async within as well regardless of the keyword!");
128/// });
129///
130/// let _handler = boxed_connect_handler!(move |destination, options, authenticator| {
131///     todo!("You can also explicitly mark to move into the closure");
132/// });
133/// ```
134#[macro_export]
135macro_rules! boxed_connect_handler {
136    (|$destination:ident, $options:ident, $authenticator:ident| $(async)? $body:block) => {{
137        let x: $crate::manager::BoxedConnectHandler = Box::new(
138            |$destination: &$crate::common::Destination,
139             $options: &$crate::common::Map,
140             $authenticator: &mut dyn $crate::auth::Authenticator| async { $body },
141        );
142        x
143    }};
144    (move |$destination:ident, $options:ident, $authenticator:ident| $(async)? $body:block) => {{
145        let x: $crate::manager::BoxedConnectHandler = Box::new(
146            move |$destination: &$crate::common::Destination,
147                  $options: &$crate::common::Map,
148                  $authenticator: &mut dyn $crate::auth::Authenticator| async move { $body },
149        );
150        x
151    }};
152}
153
154#[cfg(test)]
155mod tests {
156    use test_log::test;
157
158    use super::*;
159    use crate::common::FramedTransport;
160
161    #[inline]
162    fn test_destination() -> Destination {
163        "scheme://host:1234".parse().unwrap()
164    }
165
166    #[inline]
167    fn test_options() -> Map {
168        Map::default()
169    }
170
171    #[inline]
172    fn test_authenticator() -> impl Authenticator {
173        FramedTransport::pair(1).0
174    }
175
176    #[test(tokio::test)]
177    async fn boxed_launch_handler_should_generate_valid_boxed_launch_handler() {
178        let handler = boxed_launch_handler!(|_destination, _options, _authenticator| {
179            Err(io::Error::from(io::ErrorKind::Other))
180        });
181        assert_eq!(
182            handler
183                .launch(
184                    &test_destination(),
185                    &test_options(),
186                    &mut test_authenticator()
187                )
188                .await
189                .unwrap_err()
190                .kind(),
191            io::ErrorKind::Other
192        );
193
194        let handler = boxed_launch_handler!(|_destination, _options, _authenticator| async {
195            Err(io::Error::from(io::ErrorKind::Other))
196        });
197        assert_eq!(
198            handler
199                .launch(
200                    &test_destination(),
201                    &test_options(),
202                    &mut test_authenticator()
203                )
204                .await
205                .unwrap_err()
206                .kind(),
207            io::ErrorKind::Other
208        );
209
210        let handler = boxed_launch_handler!(move |_destination, _options, _authenticator| {
211            Err(io::Error::from(io::ErrorKind::Other))
212        });
213        assert_eq!(
214            handler
215                .launch(
216                    &test_destination(),
217                    &test_options(),
218                    &mut test_authenticator()
219                )
220                .await
221                .unwrap_err()
222                .kind(),
223            io::ErrorKind::Other
224        );
225
226        let handler = boxed_launch_handler!(move |_destination, _options, _authenticator| async {
227            Err(io::Error::from(io::ErrorKind::Other))
228        });
229        assert_eq!(
230            handler
231                .launch(
232                    &test_destination(),
233                    &test_options(),
234                    &mut test_authenticator()
235                )
236                .await
237                .unwrap_err()
238                .kind(),
239            io::ErrorKind::Other
240        );
241    }
242
243    #[test(tokio::test)]
244    async fn boxed_connect_handler_should_generate_valid_boxed_connect_handler() {
245        let handler = boxed_connect_handler!(|_destination, _options, _authenticator| {
246            Err(io::Error::from(io::ErrorKind::Other))
247        });
248        assert_eq!(
249            handler
250                .connect(
251                    &test_destination(),
252                    &test_options(),
253                    &mut test_authenticator()
254                )
255                .await
256                .unwrap_err()
257                .kind(),
258            io::ErrorKind::Other
259        );
260
261        let handler = boxed_connect_handler!(|_destination, _options, _authenticator| async {
262            Err(io::Error::from(io::ErrorKind::Other))
263        });
264        assert_eq!(
265            handler
266                .connect(
267                    &test_destination(),
268                    &test_options(),
269                    &mut test_authenticator()
270                )
271                .await
272                .unwrap_err()
273                .kind(),
274            io::ErrorKind::Other
275        );
276
277        let handler = boxed_connect_handler!(move |_destination, _options, _authenticator| {
278            Err(io::Error::from(io::ErrorKind::Other))
279        });
280        assert_eq!(
281            handler
282                .connect(
283                    &test_destination(),
284                    &test_options(),
285                    &mut test_authenticator()
286                )
287                .await
288                .unwrap_err()
289                .kind(),
290            io::ErrorKind::Other
291        );
292
293        let handler = boxed_connect_handler!(move |_destination, _options, _authenticator| async {
294            Err(io::Error::from(io::ErrorKind::Other))
295        });
296        assert_eq!(
297            handler
298                .connect(
299                    &test_destination(),
300                    &test_options(),
301                    &mut test_authenticator()
302                )
303                .await
304                .unwrap_err()
305                .kind(),
306            io::ErrorKind::Other
307        );
308    }
309}