tycho_network/util/
traits.rs

1use std::future::Future;
2
3use anyhow::Result;
4
5use crate::network::{Network, Peer};
6use crate::types::{PeerId, Request, Response};
7
8pub trait NetworkExt {
9    fn query(
10        &self,
11        peer_id: &PeerId,
12        request: Request,
13    ) -> impl Future<Output = Result<Response>> + Send;
14
15    fn send(&self, peer_id: &PeerId, request: Request) -> impl Future<Output = Result<()>> + Send;
16}
17
18impl NetworkExt for Network {
19    async fn query(&self, peer_id: &PeerId, request: Request) -> Result<Response> {
20        on_connected_peer(self, Peer::rpc, peer_id, request).await
21    }
22
23    async fn send(&self, peer_id: &PeerId, request: Request) -> Result<()> {
24        on_connected_peer(self, Peer::send_message, peer_id, request).await
25    }
26}
27
28async fn on_connected_peer<T, F>(
29    network: &Network,
30    f: F,
31    peer_id: &PeerId,
32    request: Request,
33) -> Result<T>
34where
35    for<'a> F: PeerTask<'a, T>,
36{
37    let peer = 'peer: {
38        // Interact if already connected
39        if let Some(peer) = network.peer(peer_id) {
40            break 'peer peer;
41        }
42
43        // Try to connect
44        match network.known_peers().get(peer_id) {
45            // Initiate a connection of it is a known peer
46            Some(peer_info) => {
47                // TODO: try multiple addresses
48                let address = peer_info
49                    .iter_addresses()
50                    .next()
51                    .cloned()
52                    .expect("address list must have at least one item");
53
54                network.connect(address, peer_id).await?
55            }
56            // Error otherwise
57            None => anyhow::bail!(UnknownPeerError { peer_id: *peer_id }),
58        }
59    };
60
61    f.call(&peer, request).await
62}
63
64trait PeerTask<'a, T> {
65    type Output: Future<Output = Result<T>> + 'a;
66
67    fn call(self, peer: &'a Peer, request: Request) -> Self::Output;
68}
69
70impl<'a, T, F, Fut> PeerTask<'a, T> for F
71where
72    F: FnOnce(&'a Peer, Request) -> Fut,
73    Fut: Future<Output = Result<T>> + 'a,
74{
75    type Output = Fut;
76
77    #[inline]
78    fn call(self, peer: &'a Peer, request: Request) -> Fut {
79        self(peer, request)
80    }
81}
82
83#[derive(Debug, PartialEq, Eq, thiserror::Error)]
84#[error("trying to interact with an unknown peer: {peer_id}")]
85pub struct UnknownPeerError {
86    pub peer_id: PeerId,
87}