pub trait FallbackIf<R> {
fn fallback_if<P, F>(self, predicate: P, alternative: F) -> R
where
P: Into<bool>,
F: FnOnce() -> R;
}
impl<T, E> FallbackIf<Result<T, E>> for Result<T, E> {
fn fallback_if<P, F>(self, predicate: P, alternative: F) -> Result<T, E>
where
P: Into<bool>,
F: FnOnce() -> Result<T, E>,
{
if self.is_err() && predicate.into() {
alternative()
} else {
self
}
}
}
impl<T> FallbackIf<Option<T>> for Option<T> {
fn fallback_if<P, F>(self, predicate: P, alternative: F) -> Option<T>
where
P: Into<bool>,
F: FnOnce() -> Option<T>,
{
if self.is_none() && predicate.into() {
alternative()
} else {
self
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[yare::parameterized(
ok = { Ok(1), Ok(9), Ok(1) }, // Does not enter the alternative, because input = Ok
err_to_ok = { Err(()), Ok(9), Ok(9) }, // Does enter the alternative, because input = Err and fallback pred. = true
err_to_err = { Err(()), Err(()), Err(()) }, // Does enter the alternative, because input = Err and fallback pred. = true
)]
fn result_do_fallback(
result: Result<u32, ()>,
fallback_result: Result<u32, ()>,
expected: Result<u32, ()>,
) {
let outcome = result.fallback_if(true, || fallback_result);
assert_eq!(outcome, expected)
}
#[yare::parameterized(
ok = { Ok(1), Ok(9), Ok(1) }, // Does not enter the alternative, because input = Ok
err_to_ok = { Err(()), Ok(9), Err(()) }, // Does not enter the alternative, because while input = Err, fallback pred. = false
err_to_err = { Err(()), Err(()), Err(()) }, // Does not enter the alternative, because while input = Err, fallback pred. = false
)]
fn result_do_not_fallback(
result: Result<u32, ()>,
fallback_result: Result<u32, ()>,
expected: Result<u32, ()>,
) {
let outcome = result.fallback_if(false, || fallback_result);
assert_eq!(outcome, expected)
}
#[yare::parameterized(
ok = { Some(1), Some(9), Some(1) }, // Does not enter the alternative, because input = Some
none_to_some = { None, Some(9), Some(9) }, // Does enter the alternative, because input = None and fallback pred. = true
none_to_none = { None, None, None }, // Does enter the alternative, because input = None and fallback pred. = true
)]
fn option_do_fallback(result: Option<u32>, fallback_value: Option<u32>, expected: Option<u32>) {
let outcome = result.fallback_if(true, || fallback_value);
assert_eq!(outcome, expected)
}
#[yare::parameterized(
ok = { Some(1), Some(9), Some(1) }, // Does not enter the alternative, because input = Some
none_to_some = { None, Some(9), None }, // Does not enter the alternative, because while input = None, fallback pred. = false
none_to_none = { None, None, None }, // Does not enter the alternative, because while input = None, fallback pred. = false
)]
fn option_do_not_fallback(
result: Option<u32>,
fallback_value: Option<u32>,
expected: Option<u32>,
) {
let outcome = result.fallback_if(false, || fallback_value);
assert_eq!(outcome, expected)
}
#[test]
fn readme() {
struct Config {
fallback_to_local: bool,
}
#[derive(Debug, PartialEq)]
struct Manifest;
impl Manifest {
pub fn try_fetch_remote() -> Result<Self, ()> {
Err(())
}
pub fn try_fetch_local() -> Result<Self, ()> {
Ok(Manifest)
}
}
let config = Config {
fallback_to_local: true,
};
let result = Manifest::try_fetch_remote();
let outcome = result.fallback_if(config.fallback_to_local, || Manifest::try_fetch_local());
assert_eq!(outcome, Ok(Manifest))
}
}