use futures::TryStreamExt;
use napi::bindgen_prelude::*;
use napi_derive::napi;
use crate::{Iroh, NetClient, PublicKey};
#[napi]
pub struct Net {
client: NetClient,
}
#[napi]
impl Iroh {
#[napi(getter)]
pub fn net(&self) -> Net {
let client = self.client.clone().boxed();
let client = iroh_node_util::rpc::client::net::Client::new(client);
Net { client }
}
}
#[napi]
impl Net {
#[napi]
pub async fn remote_info_list(&self) -> Result<Vec<RemoteInfo>> {
let infos = self
.client
.remote_info_iter()
.await?
.map_ok(|info| info.into())
.try_collect::<Vec<_>>()
.await?;
Ok(infos)
}
#[napi]
pub async fn remote_info(&self, node_id: &PublicKey) -> Result<Option<RemoteInfo>> {
let info = self
.client
.remote_info(node_id.into())
.await
.map(|i| i.map(|i| i.into()))?;
Ok(info)
}
#[napi]
pub async fn node_id(&self) -> Result<String> {
let id = self.client.node_id().await?;
Ok(id.to_string())
}
#[napi]
pub async fn node_addr(&self) -> Result<NodeAddr> {
let addr = self.client.node_addr().await?;
Ok(addr.into())
}
#[napi]
pub async fn add_node_addr(&self, addr: NodeAddr) -> Result<()> {
self.client.add_node_addr(addr.clone().try_into()?).await?;
Ok(())
}
#[napi]
pub async fn home_relay(&self) -> Result<Option<String>> {
let relay = self.client.home_relay().await?;
Ok(relay.map(|u| u.to_string()))
}
}
#[derive(Debug)]
#[napi(object)]
pub struct CounterStats {
pub value: u32,
pub description: String,
}
#[derive(Debug, Clone)]
#[napi(object)]
pub struct DirectAddrInfo {
pub addr: String,
pub latency: Option<u32>,
pub last_control_time: Option<u32>,
pub last_control_msg: Option<String>,
pub last_payload: Option<u32>,
pub last_alive: Option<u32>,
}
impl From<iroh::endpoint::DirectAddrInfo> for DirectAddrInfo {
fn from(value: iroh::endpoint::DirectAddrInfo) -> Self {
Self {
addr: value.addr.to_string(),
latency: value.latency.map(|d| u32::try_from(d.as_millis()).unwrap()),
last_control_time: value
.last_control
.as_ref()
.map(|(d, _)| u32::try_from(d.as_millis()).unwrap()),
last_control_msg: value.last_control.as_ref().map(|(_, m)| m.to_string()),
last_payload: value
.last_payload
.map(|d| u32::try_from(d.as_millis()).unwrap()),
last_alive: value
.last_alive
.map(|d| u32::try_from(d.as_millis()).unwrap()),
}
}
}
#[derive(Debug)]
#[napi(object)]
pub struct LatencyAndControlMsg {
pub latency: u32,
pub control_msg: String,
}
#[derive(Debug)]
#[napi(object)]
pub struct RemoteInfo {
pub node_id: Vec<u8>,
pub relay_url: Option<String>,
pub addrs: Vec<DirectAddrInfo>,
pub conn_type: ConnectionType,
pub latency: Option<u32>,
pub last_used: Option<u32>,
}
impl From<iroh::endpoint::RemoteInfo> for RemoteInfo {
fn from(value: iroh::endpoint::RemoteInfo) -> Self {
RemoteInfo {
node_id: value.node_id.as_bytes().to_vec(),
relay_url: value.relay_url.map(|info| info.relay_url.to_string()),
addrs: value.addrs.into_iter().map(|a| a.into()).collect(),
conn_type: value.conn_type.into(),
latency: value.latency.map(|d| u32::try_from(d.as_micros()).unwrap()),
last_used: value
.last_used
.map(|d| u32::try_from(d.as_micros()).unwrap()),
}
}
}
#[derive(Debug)]
#[napi(string_enum)]
pub enum ConnType {
Direct,
Relay,
Mixed,
None,
}
#[derive(Debug)]
#[napi(object)]
pub struct ConnectionType {
pub r#type: ConnType,
pub details: Option<String>,
}
impl From<iroh::endpoint::ConnectionType> for ConnectionType {
fn from(value: iroh::endpoint::ConnectionType) -> Self {
match value {
iroh::endpoint::ConnectionType::Direct(addr) => ConnectionType {
r#type: ConnType::Direct,
details: Some(addr.to_string()),
},
iroh::endpoint::ConnectionType::Mixed(addr, url) => ConnectionType {
r#type: ConnType::Mixed,
details: Some(format!("{} - {}", addr, url)),
},
iroh::endpoint::ConnectionType::Relay(url) => ConnectionType {
r#type: ConnType::Relay,
details: Some(url.to_string()),
},
iroh::endpoint::ConnectionType::None => ConnectionType {
r#type: ConnType::None,
details: None,
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[napi(object)]
pub struct NodeAddr {
pub node_id: String,
pub relay_url: Option<String>,
pub addresses: Option<Vec<String>>,
}
#[napi]
pub fn verify_node_addr(addr: NodeAddr) -> Result<()> {
let _addr: iroh::NodeAddr = addr.try_into()?;
Ok(())
}
impl TryFrom<NodeAddr> for iroh::NodeAddr {
type Error = anyhow::Error;
fn try_from(value: NodeAddr) -> anyhow::Result<Self> {
let key: iroh::PublicKey = value.node_id.parse().map_err(anyhow::Error::from)?;
let mut node_addr = iroh::NodeAddr::new(key);
let addresses = value
.addresses
.unwrap_or_default()
.into_iter()
.map(|addr| {
let addr: std::net::SocketAddr = addr.parse().map_err(anyhow::Error::from)?;
Ok(addr)
})
.collect::<anyhow::Result<Vec<_>>>()?;
if let Some(derp_url) = value.relay_url {
let url = url::Url::parse(&derp_url).map_err(anyhow::Error::from)?;
node_addr = node_addr.with_relay_url(url.into());
}
node_addr = node_addr.with_direct_addresses(addresses);
Ok(node_addr)
}
}
impl From<iroh::NodeAddr> for NodeAddr {
fn from(value: iroh::NodeAddr) -> Self {
let addresses: Vec<_> = value
.direct_addresses
.into_iter()
.map(|d| d.to_string())
.collect();
let addresses = if addresses.is_empty() {
None
} else {
Some(addresses)
};
NodeAddr {
node_id: value.node_id.to_string(),
relay_url: value.relay_url.map(|url| url.to_string()),
addresses,
}
}
}