links_core/core/
conid.rs

1use std::{
2    fmt::Display,
3    net::SocketAddr,
4    time::{Duration, Instant},
5};
6
7/// Connection identifier
8/// 
9/// #Variants
10/// * Initiator: Indicates a Clt connection or the side of the link which initiated the connection
11/// * Acceptor: Indicates a Svc connection or the side of the link which accepted the connection
12#[derive(Debug, Clone, PartialEq)]
13pub enum ConId {
14    Initiator { name: String, local: Option<SocketAddr>, peer: SocketAddr },
15    Acceptor { name: String, local: SocketAddr, peer: Option<SocketAddr> },
16}
17impl ConId {
18    pub fn clt(name: Option<&str>, local: Option<&str>, peer: &str) -> Self {
19        ConId::Initiator {
20            name: name.unwrap_or("unknown").to_owned(),
21            local: local.map(|addr| addr.parse().unwrap_or_else(|_| panic!("unable to parse addr: {:?}", addr))),
22            peer: peer.parse().unwrap_or_else(|_| panic!("unable to parse addr: {:?}", peer)),
23        }
24    }
25    pub fn set_local(&mut self, local: SocketAddr) {
26        match self {
27            ConId::Initiator { local: l, .. } => *l = Some(local),
28            ConId::Acceptor { local: l, .. } => *l = local,
29        }
30    }
31    pub fn set_peer(&mut self, peer: SocketAddr) {
32        match self {
33            ConId::Initiator { peer: p, .. } => *p = peer,
34            ConId::Acceptor { peer: p, .. } => *p = Some(peer),
35        }
36    }
37
38    pub fn svc(name: Option<&str>, local: &str, peer: Option<&str>) -> Self {
39        ConId::Acceptor {
40            name: name.unwrap_or("unknown").to_owned(),
41            local: local.parse().unwrap_or_else(|_| panic!("unable to parse addr: {:?}", local)),
42            peer: peer.map(|addr| addr.parse().unwrap_or_else(|_| panic!("unable to parse addr: {:?}", addr))),
43        }
44    }
45    pub fn name(&self) -> &str {
46        match self {
47            ConId::Initiator { name, .. } => name,
48            ConId::Acceptor { name, .. } => name,
49        }
50    }
51    pub fn get_peer(&self) -> Option<SocketAddr> {
52        match self {
53            ConId::Initiator { peer, .. } => Some(*peer),
54            ConId::Acceptor { peer, .. } => *peer,
55        }
56    }
57    pub fn get_local(&self) -> Option<SocketAddr> {
58        match self {
59            ConId::Initiator { local, .. } => *local,
60            ConId::Acceptor { local, .. } => Some(*local),
61        }
62    }
63    pub fn from_same_lineage(&self, other: &Self) -> bool {
64        match (self, other) {
65            // listening ports are unique hence must be ( self IS other | other is a Clt that was started by self )
66            (ConId::Acceptor { local: l1, .. }, ConId::Acceptor { local: l2, .. }) => l1 == l2, 
67            _ => false,
68        }
69    }
70}
71impl Default for ConId {
72    fn default() -> Self {
73        ConId::clt(None, None, "0.0.0.0:0")
74    }
75}
76impl Display for ConId {
77    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        match self {
79            ConId::Initiator { name, local, peer } => {
80                write!(
81                    f,
82                    "Initiator({name}@{}->{peer})",
83                    match local {
84                        Some(local) => format!("{}", local),
85                        None => "pending".to_owned(),
86                    }
87                )
88            }
89            ConId::Acceptor { name, local, peer } => {
90                write!(
91                    f,
92                    "Acceptor({name}@{local}<-{})",
93                    match peer {
94                        Some(peer) => format!("{}", peer),
95                        None => "pending".to_owned(),
96                    }
97                )
98            }
99        }
100    }
101}
102
103/// Trait that maintain an active connection can disclose its connection information via [ConId] struct
104pub trait ConnectionId {
105    fn con_id(&self) -> &ConId;
106}
107
108pub trait ConnectionStatus {
109    /// logical check of connection status
110    fn is_connected(&self) -> bool;
111    fn is_connected_busywait_timeout(&self, timeout: Duration) -> bool {
112        let start = Instant::now();
113        while start.elapsed() < timeout {
114            if self.is_connected() {
115                return true;
116            }
117            std::hint::spin_loop();
118        }
119        // can't assume false at this point and need to recheck in case timeout arg is Duration::ZERO
120        self.is_connected() 
121    }
122}
123/// Provides methods for testing status of connections in the Pool
124pub trait PoolConnectionStatus {
125    fn is_next_connected(&mut self) -> bool;
126    fn is_next_connected_busywait_timeout(&mut self, timeout: Duration) -> bool {
127        let start = Instant::now();
128        while start.elapsed() < timeout {
129            if self.is_next_connected() {
130                return true;
131            }
132            std::hint::spin_loop();
133        }
134        // can't assume false at this point and need to recheck in case timeout arg is Duration::ZERO
135        self.is_next_connected()
136    }
137    fn all_connected(&mut self) -> bool;
138    fn all_connected_busywait_timeout(&mut self, timeout: Duration) -> bool {
139        let start = Instant::now();
140        while start.elapsed() < timeout {
141            if self.all_connected() {
142                return true;
143            }
144            std::hint::spin_loop();
145        }
146        // can't assume false at this point and need to recheck in case timeout arg is Duration::ZERO
147        self.all_connected()
148    }
149}
150
151#[cfg(test)]
152mod test {
153
154    use log::info;
155
156    use crate::prelude::*;
157    use crate::unittest::setup;
158
159    #[test]
160    fn test_con_id() {
161        setup::log::configure();
162        let con_id = ConId::clt(Some("unittest"), None, "0.0.0.0:1");
163        info!("con_id: {:?}", con_id);
164        info!("con_id: {}", con_id);
165        assert_eq!(con_id.to_string(), "Initiator(unittest@pending->0.0.0.0:1)");
166
167        let con_id = ConId::svc(Some("unittest"), "0.0.0.0:1", None);
168        info!("con_id: {:?}", con_id);
169        info!("con_id: {}", con_id);
170        assert_eq!(con_id.to_string(), "Acceptor(unittest@0.0.0.0:1<-pending)");
171    }
172}