use async_trait::async_trait;
use rand::RngExt as _;
use crate::error::{ProxyError, ProxyResult};
use crate::strategy::{ProxyCandidate, RotationStrategy, healthy_candidates};
#[derive(Debug, Default, Clone, Copy)]
pub struct WeightedStrategy;
#[async_trait]
impl RotationStrategy for WeightedStrategy {
async fn select<'a>(
&self,
candidates: &'a [ProxyCandidate],
) -> ProxyResult<&'a ProxyCandidate> {
let healthy: Vec<&ProxyCandidate> = healthy_candidates(candidates)
.into_iter()
.filter(|c| c.weight > 0)
.collect();
if healthy.is_empty() {
return Err(ProxyError::AllProxiesUnhealthy);
}
let total: u64 = healthy.iter().map(|c| u64::from(c.weight)).sum();
let mut cursor: u64 = rand::rng().random_range(0..total);
for candidate in &healthy {
if cursor < u64::from(candidate.weight) {
return Ok(candidate);
}
cursor -= u64::from(candidate.weight);
}
healthy
.last()
.copied()
.ok_or(crate::error::ProxyError::AllProxiesUnhealthy)
}
}