use crate::{Answers, Error, ErrorKind, Provider, ProviderFuture, Request, Result, ask};
use std::sync::Arc;
pub struct Cascade {
providers: Vec<Arc<dyn Provider>>,
escalate_below: f64,
}
impl Cascade {
pub fn new(providers: Vec<Arc<dyn Provider>>, escalate_below: f64) -> Result<Self> {
if providers.len() < 2
|| !escalate_below.is_finite()
|| !(0.0..=1.0).contains(&escalate_below)
{
return Err(Error::new(
ErrorKind::InvalidArgument,
"cascade needs two providers and a threshold from zero to one",
));
}
Ok(Self {
providers,
escalate_below,
})
}
}
impl Provider for Cascade {
fn ask<'a>(&'a self, request: &'a Request) -> ProviderFuture<'a> {
Box::pin(async move {
let mut pending = request.questions().clone();
let mut results = Answers::new();
for provider in &self.providers {
let hop = request.with_questions(pending.clone())?;
let answers = ask(provider, &hop).await?;
pending.retain(|name, _| {
answers.get(name).is_some_and(|answer| {
!answer.decided() || answer.confidence() < self.escalate_below
})
});
results.extend(answers);
if pending.is_empty() {
break;
}
}
crate::provider::validate_answers(request, results)
})
}
}
#[cfg(feature = "tokio")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
pub struct TimeLimited<P> {
provider: P,
duration: std::time::Duration,
}
#[cfg(feature = "tokio")]
impl<P: Provider> TimeLimited<P> {
pub fn new(provider: P, duration: std::time::Duration) -> Result<Self> {
if duration.is_zero() {
return Err(Error::new(
ErrorKind::InvalidArgument,
"timeout must be positive",
));
}
Ok(Self { provider, duration })
}
}
#[cfg(feature = "tokio")]
impl<P: Provider> Provider for TimeLimited<P> {
fn ask<'a>(&'a self, request: &'a Request) -> ProviderFuture<'a> {
Box::pin(async move {
crate::provider::deadline(self.duration, ask(&self.provider, request)).await?
})
}
}