use std::collections::BTreeMap;
use reqwest::Method;
use serde::{Deserialize, Deserializer};
use crate::client::request;
use crate::swarm::Error;
use super::DebugApi;
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct Peer {
pub address: String,
#[serde(default, rename = "fullNode")]
pub full_node: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Addresses {
pub overlay: String,
pub underlay: Vec<String>,
pub ethereum: String,
pub public_key: String,
pub pss_public_key: String,
}
#[derive(Clone, Debug, PartialEq, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MetricSnapshotView {
#[serde(default)]
pub last_seen_timestamp: i64,
#[serde(default)]
pub session_connection_retry: u64,
#[serde(default)]
pub connection_total_duration: f64,
#[serde(default)]
pub session_connection_duration: f64,
#[serde(default)]
pub session_connection_direction: String,
#[serde(default, rename = "latencyEWMA")]
pub latency_ewma: i64,
#[serde(default)]
pub reachability: String,
#[serde(default)]
pub healthy: bool,
}
#[derive(Clone, Debug, PartialEq, Default, Deserialize)]
pub struct PeerInfo {
pub address: String,
#[serde(default)]
pub metrics: Option<MetricSnapshotView>,
}
#[derive(Clone, Debug, PartialEq, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BinInfo {
#[serde(default, rename = "population")]
pub population: u64,
#[serde(default, rename = "connected")]
pub connected: u64,
#[serde(default, deserialize_with = "deserialize_null_or_seq")]
pub connected_peers: Vec<PeerInfo>,
#[serde(default, deserialize_with = "deserialize_null_or_seq")]
pub disconnected_peers: Vec<PeerInfo>,
}
fn deserialize_null_or_seq<'de, D, T>(d: D) -> Result<Vec<T>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
Ok(Option::<Vec<T>>::deserialize(d)?.unwrap_or_default())
}
#[derive(Clone, Debug, PartialEq, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Topology {
pub base_addr: String,
pub population: i64,
pub connected: i64,
pub timestamp: String,
pub nn_low_watermark: i64,
pub depth: u8,
#[serde(default)]
pub reachability: String,
#[serde(default)]
pub network_availability: String,
#[serde(default = "default_bins", deserialize_with = "deserialize_bins")]
pub bins: Vec<BinInfo>,
#[serde(default)]
pub light_nodes: BinInfo,
}
fn default_bins() -> Vec<BinInfo> {
vec![BinInfo::default(); 32]
}
fn deserialize_bins<'de, D>(d: D) -> Result<Vec<BinInfo>, D::Error>
where
D: Deserializer<'de>,
{
let map: Option<BTreeMap<String, BinInfo>> = Option::deserialize(d)?;
let mut map = map.unwrap_or_default();
let mut out = Vec::with_capacity(32);
for i in 0..32u8 {
let key = format!("bin_{i}");
out.push(map.remove(&key).unwrap_or_default());
}
Ok(out)
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReserveState {
pub radius: u8,
pub storage_radius: u8,
pub commitment: i64,
}
impl DebugApi {
pub async fn peers(&self) -> Result<Vec<Peer>, Error> {
let builder = request(&self.inner, Method::GET, "peers")?;
#[derive(Deserialize)]
struct Resp {
peers: Vec<Peer>,
}
let r: Resp = self.inner.send_json(builder).await?;
Ok(r.peers)
}
pub async fn blocklist(&self) -> Result<Vec<Peer>, Error> {
let builder = request(&self.inner, Method::GET, "blocklist")?;
#[derive(Deserialize)]
struct Resp {
peers: Vec<Peer>,
}
let r: Resp = self.inner.send_json(builder).await?;
Ok(r.peers)
}
pub async fn remove_peer(&self, address: &str) -> Result<(), Error> {
let path = format!("peers/{address}");
let builder = request(&self.inner, Method::DELETE, &path)?;
self.inner.send(builder).await?;
Ok(())
}
pub async fn ping_peer(&self, address: &str) -> Result<String, Error> {
let path = format!("pingpong/{address}");
let builder = request(&self.inner, Method::POST, &path)?;
#[derive(Deserialize)]
struct Resp {
rtt: String,
}
let r: Resp = self.inner.send_json(builder).await?;
Ok(r.rtt)
}
pub async fn connect_peer(&self, multiaddr: &str) -> Result<String, Error> {
let trimmed = multiaddr.trim_start_matches('/');
let path = format!("connect/{trimmed}");
let builder = request(&self.inner, Method::POST, &path)?;
#[derive(Deserialize)]
struct Resp {
address: String,
}
let r: Resp = self.inner.send_json(builder).await?;
Ok(r.address)
}
pub async fn addresses(&self) -> Result<Addresses, Error> {
let builder = request(&self.inner, Method::GET, "addresses")?;
self.inner.send_json(builder).await
}
pub async fn topology(&self) -> Result<Topology, Error> {
let builder = request(&self.inner, Method::GET, "topology")?;
self.inner.send_json(builder).await
}
pub async fn reserve_state(&self) -> Result<ReserveState, Error> {
let builder = request(&self.inner, Method::GET, "reservestate")?;
self.inner.send_json(builder).await
}
pub async fn welcome_message(&self) -> Result<String, Error> {
let builder = request(&self.inner, Method::GET, "welcome-message")?;
#[derive(Deserialize)]
struct Resp {
#[serde(rename = "welcomeMessage")]
welcome_message: String,
}
let r: Resp = self.inner.send_json(builder).await?;
Ok(r.welcome_message)
}
pub async fn set_welcome_message(&self, message: &str) -> Result<(), Error> {
#[derive(serde::Serialize)]
struct Body<'a> {
#[serde(rename = "welcomeMessage")]
welcome_message: &'a str,
}
let body = serde_json::to_vec(&Body {
welcome_message: message,
})?;
let builder = request(&self.inner, Method::POST, "welcome-message")?
.header("Content-Type", "application/json")
.body(bytes::Bytes::from(body));
self.inner.send(builder).await?;
Ok(())
}
}