use itertools::{join, zip}; use std::fmt::{Debug, Display}; use std::future::Future; #[derive(Clone)] pub struct Fallback { pub servers: Vec, } #[derive(Debug, PartialEq)] pub enum FallbackError { AllErrored(Vec), } impl Fallback { pub fn new(servers: Vec) -> Self { Self { servers } } /// Return the first successful result along with number of previous errors encountered /// or all the errors encountered if every server fails. pub async fn first_success<'a, F, O, E, R>( &'a self, func: F, ) -> Result<(O, usize), FallbackError> where F: Fn(&'a T) -> R, R: Future>, { let mut errors = vec![]; for server in &self.servers { match func(server).await { Ok(val) => return Ok((val, errors.len())), Err(e) => errors.push(e), } } Err(FallbackError::AllErrored(errors)) } pub fn map_format_error<'a, E, F, S>(&'a self, f: F, error: &FallbackError) -> String where F: FnMut(&'a T) -> &'a S, S: Display + 'a, E: Debug, { match error { FallbackError::AllErrored(v) => format!( "All fallback errored: {}", join( zip(self.servers.iter().map(f), v.iter()) .map(|(server, error)| format!("{} => {:?}", server, error)), ", " ) ), } } } impl Fallback { pub fn format_error(&self, error: &FallbackError) -> String { self.map_format_error(|s| s, error) } }