use ergo_chain_types::ConnectionDirection;
use ergo_chain_types::PeerAddr;
use serde::{de::Error, Deserialize, Deserializer, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
#[repr(C)]
pub struct PeerInfo {
#[serde(
rename = "address",
deserialize_with = "parse_peer_addr_with_leading_slash"
)]
pub addr: PeerAddr,
#[serde(rename = "lastMessage")]
pub last_message: u64,
#[serde(rename = "lastHandshake")]
pub last_handshake: u64, pub name: String,
#[serde(rename = "connectionType")]
pub conn_type: Option<ConnectionDirection>,
}
fn parse_peer_addr_with_leading_slash<'de, D>(deserializer: D) -> Result<PeerAddr, D::Error>
where
D: Deserializer<'de>,
{
let s: &str = Deserialize::deserialize(deserializer)?;
let s = if s.starts_with('/') {
let mut chars = s.chars();
chars.next();
chars.as_str()
} else {
s
};
match s.parse().map_err(D::Error::custom) {
Ok(peer_addr) => Ok(peer_addr),
Err(_) => {
let parts: Vec<&str> = s.rsplit(':').collect();
if parts.len() == 2 {
return Err(D::Error::custom(format!(
"expected invalid IP v6(without brackets) address, got: {}",
s
)));
}
#[allow(clippy::unwrap_used)]
let port = parts.first().cloned().unwrap();
let host: String = parts
.into_iter()
.skip(1)
.rev()
.collect::<Vec<&str>>()
.join(":");
let str = format!("[{}]:{}", host, port);
str.parse().map_err(D::Error::custom)
}
}
}
#[cfg(test)]
#[cfg(feature = "json")]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn test_parse_peer_info() {
let json = "{\n \"address\" : \"/62.106.112.158:9030\",\n \"lastMessage\" : 0,\n \"lastHandshake\" : 0,\n \"name\" : \"ergo-mainnet-4.0.12\",\n \"connectionType\" : null\n }";
let _: PeerInfo = serde_json::from_str(json).unwrap();
}
#[test]
fn test_parse_peer_info_ipv6_pre_jkd14() {
let json = "{\n \"address\" : \"/2a0d:6fc0:7cb:be00:50be:7d74:7a00:aa3e:9030\",\n \"lastMessage\" : 0,\n \"lastHandshake\" : 0,\n \"name\" : \"ergo-mainnet-4.0.12\",\n \"connectionType\" : null\n }";
let _: PeerInfo = serde_json::from_str(json).unwrap();
}
}