use std::time::{Duration, Instant};
use serde_json::Value;
use super::rpc::rpc_call;
use super::types::RouterInfoResult;
const ROUTER_INFO_KEYS_BATCH_1: &[&str] = &[
"i2p.router.status", "i2p.router.version", "i2p.router.uptime", "i2p.router.net.bw.inbound.1s", "i2p.router.net.bw.inbound.15s", "i2p.router.net.bw.outbound.1s", "i2p.router.net.bw.outbound.15s", "i2p.router.net.bw.transit.15s", "i2p.router.net.status", "i2p.router.net.status.v6", "i2p.router.net.error", "i2p.router.net.error.v6", "i2p.router.net.testing", "i2p.router.net.testing.v6", ];
const ROUTER_INFO_KEYS_BATCH_2: &[&str] = &[
"i2p.router.net.tunnels.participating", "i2p.router.net.tunnels.inbound", "i2p.router.net.tunnels.outbound", "i2p.router.net.tunnels.successrate", "i2p.router.net.tunnels.totalsuccessrate", "i2p.router.net.tunnels.queue", "i2p.router.net.tunnels.tbmqueue", "i2p.router.netdb.activepeers", "i2p.router.netdb.knownpeers", "i2p.router.netdb.floodfills", "i2p.router.netdb.leasesets", "i2p.router.net.total.received.bytes", "i2p.router.net.total.sent.bytes", "i2p.router.net.total.transit.bytes", ];
fn build_router_info_params(keys: &[&str]) -> Value {
let mut params = serde_json::Map::new();
for key in keys {
params.insert((*key).to_string(), Value::String(String::new()));
}
Value::Object(params)
}
pub struct I2pControlClient {
pub api_client: reqwest::Client, pub api_url: String, pub max_scrape_timeout: Duration, }
impl I2pControlClient {
pub fn new(api_client: reqwest::Client, api_url: String, max_scrape_timeout: Duration) -> Self {
I2pControlClient {
api_client,
api_url,
max_scrape_timeout,
}
}
pub async fn fetch_router_info(
&self,
overall_timeout: Duration,
) -> Result<RouterInfoResult, Box<dyn std::error::Error + Send + Sync>> {
let deadline = Instant::now() + overall_timeout;
let mut combined = RouterInfoResult::default();
for (batch_idx, keys) in [ROUTER_INFO_KEYS_BATCH_1, ROUTER_INFO_KEYS_BATCH_2]
.iter()
.enumerate()
{
let now = Instant::now();
let rem = if now >= deadline {
Duration::from_millis(0)
} else {
deadline.saturating_duration_since(now)
};
if rem.is_zero() {
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
format!(
"deadline exceeded before RouterInfo batch {}",
batch_idx + 1
),
)
.into());
}
let params = build_router_info_params(keys);
let data = rpc_call::<RouterInfoResult>(
&self.api_client,
&self.api_url,
"RouterInfo",
params,
rem,
)
.await
.map_err(|err| -> Box<dyn std::error::Error + Send + Sync> { Box::new(err) })?;
combined.merge_from(data);
}
Ok(combined)
}
}