use std::future::Future;
use crate::policy::Policy;
pub struct Fallback<F, T> {
fallback_fn: F,
_marker: std::marker::PhantomData<T>,
}
impl<F, T> Clone for Fallback<F, T>
where
F: Clone,
{
fn clone(&self) -> Self {
Self {
fallback_fn: self.fallback_fn.clone(),
_marker: std::marker::PhantomData,
}
}
}
impl<F, T> Fallback<F, T>
where
F: Fn() -> T + Send + Sync,
T: Send,
{
pub fn new(fallback_fn: F) -> Self {
Self {
fallback_fn,
_marker: std::marker::PhantomData,
}
}
}
#[async_trait::async_trait]
impl<F, T, E> Policy<E> for Fallback<F, T>
where
F: Fn() -> T + Send + Sync,
T: Send + Sync + Clone,
E: Send + Sync,
{
async fn execute<Func, Fut, R>(&self, f: Func) -> Result<R, E>
where
Func: Fn() -> Fut + Send + Sync,
Fut: Future<Output = Result<R, E>> + Send,
R: Send,
{
f().await
}
}
pub struct FallbackValue<T> {
value: T,
}
impl<T: Clone> Clone for FallbackValue<T> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
}
}
}
impl<T> FallbackValue<T>
where
T: Clone + Send + Sync,
{
pub fn new(value: T) -> Self {
Self { value }
}
}
#[async_trait::async_trait]
impl<T, E> Policy<E> for FallbackValue<T>
where
T: Clone + Send + Sync,
E: Send + Sync,
{
async fn execute<F, Fut, R>(&self, f: F) -> Result<R, E>
where
F: Fn() -> Fut + Send + Sync,
Fut: Future<Output = Result<R, E>> + Send,
R: Send,
{
f().await
}
}
pub trait FallbackExt<T, E> {
fn or_fallback(self, fallback: T) -> Result<T, E>;
fn or_fallback_with<F: FnOnce() -> T>(self, f: F) -> Result<T, E>;
}
impl<T, E> FallbackExt<T, E> for Result<T, E> {
fn or_fallback(self, fallback: T) -> Result<T, E> {
match self {
Ok(v) => Ok(v),
Err(_) => Ok(fallback),
}
}
fn or_fallback_with<F: FnOnce() -> T>(self, f: F) -> Result<T, E> {
match self {
Ok(v) => Ok(v),
Err(_) => Ok(f()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_fallback_ext_on_error() {
let result: Result<String, &str> = Err("error");
let with_fallback = result.or_fallback("default".to_string());
assert_eq!(with_fallback.unwrap(), "default");
}
#[tokio::test]
async fn test_fallback_ext_on_success() {
let result: Result<String, &str> = Ok("success".to_string());
let with_fallback = result.or_fallback("default".to_string());
assert_eq!(with_fallback.unwrap(), "success");
}
#[tokio::test]
async fn test_fallback_ext_with_fn() {
let result: Result<Vec<i32>, &str> = Err("error");
let with_fallback = result.or_fallback_with(|| vec![1, 2, 3]);
assert_eq!(with_fallback.unwrap(), vec![1, 2, 3]);
}
}