libp2p_core/transport/
choice.rs

1// Copyright 2017 Parity Technologies (UK) Ltd.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21use crate::either::EitherFuture;
22use crate::transport::{DialOpts, ListenerId, Transport, TransportError, TransportEvent};
23use either::Either;
24use futures::future;
25use multiaddr::Multiaddr;
26use std::{pin::Pin, task::Context, task::Poll};
27
28/// Struct returned by `or_transport()`.
29#[derive(Debug, Copy, Clone)]
30#[pin_project::pin_project]
31pub struct OrTransport<A, B>(#[pin] A, #[pin] B);
32
33impl<A, B> OrTransport<A, B> {
34    pub fn new(a: A, b: B) -> OrTransport<A, B> {
35        OrTransport(a, b)
36    }
37}
38
39impl<A, B> Transport for OrTransport<A, B>
40where
41    B: Transport,
42    A: Transport,
43{
44    type Output = future::Either<A::Output, B::Output>;
45    type Error = Either<A::Error, B::Error>;
46    type ListenerUpgrade = EitherFuture<A::ListenerUpgrade, B::ListenerUpgrade>;
47    type Dial = EitherFuture<A::Dial, B::Dial>;
48
49    fn listen_on(
50        &mut self,
51        id: ListenerId,
52        addr: Multiaddr,
53    ) -> Result<(), TransportError<Self::Error>> {
54        tracing::trace!(
55            address=%addr,
56            "Attempting to listen on address using {}",
57            std::any::type_name::<A>()
58        );
59        let addr = match self.0.listen_on(id, addr) {
60            Err(TransportError::MultiaddrNotSupported(addr)) => {
61                tracing::debug!(
62                    address=%addr,
63                    "Failed to listen on address using {}",
64                    std::any::type_name::<A>()
65                );
66                addr
67            }
68            res => return res.map_err(|err| err.map(Either::Left)),
69        };
70
71        tracing::trace!(
72            address=%addr,
73            "Attempting to listen on address using {}",
74            std::any::type_name::<B>()
75        );
76        let addr = match self.1.listen_on(id, addr) {
77            Err(TransportError::MultiaddrNotSupported(addr)) => {
78                tracing::debug!(
79                    address=%addr,
80                    "Failed to listen on address using {}",
81                    std::any::type_name::<B>()
82                );
83                addr
84            }
85            res => return res.map_err(|err| err.map(Either::Right)),
86        };
87
88        Err(TransportError::MultiaddrNotSupported(addr))
89    }
90
91    fn remove_listener(&mut self, id: ListenerId) -> bool {
92        self.0.remove_listener(id) || self.1.remove_listener(id)
93    }
94
95    fn dial(
96        &mut self,
97        addr: Multiaddr,
98        opts: DialOpts,
99    ) -> Result<Self::Dial, TransportError<Self::Error>> {
100        tracing::trace!(
101            address=%addr,
102            "Attempting to dial using {}",
103            std::any::type_name::<A>()
104        );
105        let addr = match self.0.dial(addr, opts) {
106            Ok(connec) => return Ok(EitherFuture::First(connec)),
107            Err(TransportError::MultiaddrNotSupported(addr)) => {
108                tracing::debug!(
109                    address=%addr,
110                    "Failed to dial using {}",
111                    std::any::type_name::<B>(),
112                );
113                addr
114            }
115            Err(TransportError::Other(err)) => {
116                return Err(TransportError::Other(Either::Left(err)))
117            }
118        };
119
120        tracing::trace!(
121            address=%addr,
122            "Attempting to dial {}",
123            std::any::type_name::<A>()
124        );
125        let addr = match self.1.dial(addr, opts) {
126            Ok(connec) => return Ok(EitherFuture::Second(connec)),
127            Err(TransportError::MultiaddrNotSupported(addr)) => {
128                tracing::debug!(
129                    address=%addr,
130                    "Failed to dial using {}",
131                    std::any::type_name::<B>(),
132                );
133                addr
134            }
135            Err(TransportError::Other(err)) => {
136                return Err(TransportError::Other(Either::Right(err)))
137            }
138        };
139
140        Err(TransportError::MultiaddrNotSupported(addr))
141    }
142
143    fn poll(
144        self: Pin<&mut Self>,
145        cx: &mut Context<'_>,
146    ) -> Poll<TransportEvent<Self::ListenerUpgrade, Self::Error>> {
147        let this = self.project();
148        match this.0.poll(cx) {
149            Poll::Ready(ev) => {
150                return Poll::Ready(ev.map_upgrade(EitherFuture::First).map_err(Either::Left))
151            }
152            Poll::Pending => {}
153        }
154        match this.1.poll(cx) {
155            Poll::Ready(ev) => {
156                return Poll::Ready(ev.map_upgrade(EitherFuture::Second).map_err(Either::Right))
157            }
158            Poll::Pending => {}
159        }
160        Poll::Pending
161    }
162}