use std::time::{Duration, Instant};
use async_trait::async_trait;
use futures::stream::{FuturesUnordered, StreamExt};
use serde_json::Value;
use crate::decode::ValueExt;
use crate::error::{Error, Result};
use crate::rpc::{Handler, Rpc};
#[derive(Default)]
pub struct RpcList(pub Vec<Rpc>);
#[async_trait]
impl Handler for RpcList {
async fn call(&self, method: &str, params: Vec<Value>) -> Result<Value> {
if self.0.is_empty() {
return Err(Error::NoAvailableServer);
}
let mut last_err: Option<Error> = None;
for srv in &self.0 {
match srv.call(method, params.clone()).await {
Ok(res) => return Ok(res),
Err(err) => {
if err.is_rpc_error() {
return Err(err);
}
last_err = Some(err);
}
}
}
Err(last_err.unwrap_or(Error::NoAvailableServer))
}
}
async fn probe(host: String) -> Result<Rpc> {
let mut r = Rpc::new(host);
let start = Instant::now();
let block = r.call("eth_blockNumber", vec![]).await?.to_u64()?;
r.set_probe(start.elapsed(), block);
Ok(r)
}
const SELECTION_GRACE: Duration = Duration::from_millis(200);
pub async fn evaluate(servers: &[&str]) -> Result<Box<dyn Handler>> {
match servers.len() {
0 => Err(Error::NoAvailableServer),
1 => {
let r = probe(servers[0].to_string()).await?;
Ok(Box::new(r))
}
_ => {
let mut futs: FuturesUnordered<_> =
servers.iter().map(|s| probe(s.to_string())).collect();
let mut res: Vec<Rpc> = Vec::new();
let mut last_err: Option<Error> = None;
let mut grace = std::pin::pin!(futures::future::OptionFuture::from(None));
loop {
tokio::select! {
biased;
_ = grace.as_mut(), if !res.is_empty() => {
return Ok(Box::new(RpcList(res)));
}
next = futs.next() => match next {
None => {
if !res.is_empty() {
return Ok(Box::new(RpcList(res)));
}
return Err(last_err.unwrap_or(Error::NoAvailableServer));
}
Some(Ok(r)) => {
let first = res.is_empty();
res.push(r);
if first {
grace.set(Some(tokio::time::sleep(SELECTION_GRACE)).into());
}
}
Some(Err(e)) => {
last_err = Some(e);
}
}
}
}
}
}
}