use super::{SelectionContext, Strategy};
pub struct WithFallback<P, F> {
primary: P,
fallback: F,
}
impl<P: std::fmt::Debug, F: std::fmt::Debug> std::fmt::Debug for WithFallback<P, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WithFallback")
.field("primary", &self.primary)
.field("fallback", &self.fallback)
.finish()
}
}
impl<P, F> WithFallback<P, F> {
pub fn new(primary: P, fallback: F) -> Self {
Self { primary, fallback }
}
}
impl<N, P, F> Strategy<N> for WithFallback<P, F>
where
P: Strategy<N>,
F: Strategy<N>,
{
fn select(&self, candidates: &[N], ctx: &SelectionContext) -> Option<usize> {
self.primary
.select(candidates, ctx)
.or_else(|| self.fallback.select(candidates, ctx))
}
}
pub struct FallbackChain<N> {
strategies: Vec<Box<dyn Strategy<N> + Send + Sync>>,
}
impl<N> std::fmt::Debug for FallbackChain<N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FallbackChain")
.field("strategies", &self.strategies.len())
.finish()
}
}
impl<N> FallbackChain<N> {
pub fn new(strategies: Vec<Box<dyn Strategy<N> + Send + Sync>>) -> Self {
Self { strategies }
}
}
impl<N> Strategy<N> for FallbackChain<N> {
fn select(&self, candidates: &[N], ctx: &SelectionContext) -> Option<usize> {
self.strategies
.iter()
.find_map(|s| s.select(candidates, ctx))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::strategy::RoundRobin;
#[test]
fn with_fallback_propagates_exclude() {
let strategy = WithFallback::new(RoundRobin::new(), RoundRobin::new());
let nodes = [1, 2];
let ctx = SelectionContext::builder().exclude(vec![0, 1]).build();
assert_eq!(strategy.select(&nodes, &ctx), None);
}
#[test]
fn fallback_chain_propagates_exclude() {
let chain: FallbackChain<i32> = FallbackChain::new(vec![
Box::new(RoundRobin::new()),
Box::new(RoundRobin::new()),
]);
let nodes = [1, 2];
let ctx = SelectionContext::builder().exclude(vec![0, 1]).build();
assert_eq!(chain.select(&nodes, &ctx), None);
}
#[test]
fn empty_candidates_returns_none() {
let strategy = WithFallback::new(RoundRobin::new(), RoundRobin::new());
let nodes: [i32; 0] = [];
let ctx = SelectionContext::default();
assert_eq!(strategy.select(&nodes, &ctx), None);
}
#[test]
fn fallback_used_when_primary_excludes_all() {
let strategy = WithFallback::new(RoundRobin::new(), RoundRobin::new());
let nodes = [1, 2];
let ctx = SelectionContext::builder().exclude(vec![0, 1]).build();
assert_eq!(strategy.select(&nodes, &ctx), None);
}
}