use std::future::Future;
pub trait AsyncResultExt<T, E> {
fn async_map<U, F, Fut>(self, op: F) -> impl Future<Output = Result<U, E>>
where
F: FnOnce(T) -> Fut,
Fut: Future<Output = U>;
fn async_and_then<U, F, Fut>(self, op: F) -> impl Future<Output = Result<U, E>>
where
F: FnOnce(T) -> Fut,
Fut: Future<Output = Result<U, E>>;
fn async_map_or<U, F, Fut>(self, default: U, op: F) -> impl Future<Output = U>
where
F: FnOnce(T) -> Fut,
Fut: Future<Output = U>;
fn async_map_or_else<U, D, F, Fut, DefFut>(self, default: D, op: F) -> impl Future<Output = U>
where
D: FnOnce(E) -> DefFut,
F: FnOnce(T) -> Fut,
DefFut: Future<Output = U>,
Fut: Future<Output = U>;
fn async_map_err<F, Fut, O>(self, op: F) -> impl Future<Output = Result<T, O>>
where
F: FnOnce(E) -> Fut,
Fut: Future<Output = O>;
fn async_inspect<F, Fut>(self, op: F) -> impl Future<Output = Self>
where
F: FnOnce(&T) -> Fut,
Fut: Future<Output = ()>;
fn async_inspect_err<F, Fut>(self, op: F) -> impl Future<Output = Self>
where
F: FnOnce(&E) -> Fut,
Fut: Future<Output = ()>;
fn async_is_ok_and<F, Fut>(self, op: F) -> impl Future<Output = bool>
where
F: FnOnce(T) -> Fut,
Fut: Future<Output = bool>;
fn async_is_err_and<F, Fut>(self, op: F) -> impl Future<Output = bool>
where
F: FnOnce(E) -> Fut,
Fut: Future<Output = bool>;
}
impl<T, E> AsyncResultExt<T, E> for Result<T, E> {
async fn async_map<U, F, Fut>(self, op: F) -> Result<U, E>
where
F: FnOnce(T) -> Fut,
Fut: Future<Output = U>,
{
match self {
Ok(value) => Ok(op(value).await),
Err(err) => Err(err),
}
}
async fn async_and_then<U, F, Fut>(self, op: F) -> Result<U, E>
where
F: FnOnce(T) -> Fut,
Fut: Future<Output = Result<U, E>>,
{
match self {
Ok(value) => op(value).await,
Err(err) => Err(err),
}
}
async fn async_map_or<U, F, Fut>(self, default: U, op: F) -> U
where
F: FnOnce(T) -> Fut,
Fut: Future<Output = U>,
{
match self {
Ok(value) => op(value).await,
Err(_) => default,
}
}
async fn async_map_or_else<U, D, F, Fut, DefFut>(self, default: D, op: F) -> U
where
D: FnOnce(E) -> DefFut,
F: FnOnce(T) -> Fut,
DefFut: Future<Output = U>,
Fut: Future<Output = U>,
{
match self {
Ok(value) => op(value).await,
Err(err) => default(err).await,
}
}
async fn async_map_err<F, Fut, O>(self, op: F) -> Result<T, O>
where
F: FnOnce(E) -> Fut,
Fut: Future<Output = O>,
{
match self {
Ok(value) => Ok(value),
Err(err) => Err(op(err).await),
}
}
async fn async_inspect<F, Fut>(self, op: F) -> Self
where
F: FnOnce(&T) -> Fut,
Fut: Future<Output = ()>,
{
if let Ok(ref value) = self {
op(value).await;
}
self
}
async fn async_inspect_err<F, Fut>(self, op: F) -> Self
where
F: FnOnce(&E) -> Fut,
Fut: Future<Output = ()>,
{
if let Err(ref err) = self {
op(err).await;
}
self
}
async fn async_is_ok_and<F, Fut>(self, op: F) -> bool
where
F: FnOnce(T) -> Fut,
Fut: Future<Output = bool>,
{
{
match self {
Err(_) => false,
Ok(v) => op(v).await,
}
}
}
async fn async_is_err_and<F, Fut>(self, op: F) -> bool
where
F: FnOnce(E) -> Fut,
Fut: Future<Output = bool>,
{
{
match self {
Ok(_) => false,
Err(e) => op(e).await,
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_async_map() {
let r: Result<i32, &str> = Ok(2);
let res = r.async_map(|v| async move { v * 3 }).await;
assert_eq!(res, Ok(6));
let r: Result<i32, &str> = Err("error");
let res = r.async_map(|v| async move { v * 3 }).await;
assert_eq!(res, Err("error"));
}
#[tokio::test]
async fn test_async_and_then() {
let r: Result<i32, &str> = Ok(2);
let res = r.async_and_then(|v| async move { Ok(v * 5) }).await;
assert_eq!(res, Ok(10));
let r: Result<i32, &str> = Err("fail");
let res = r.async_and_then(|v| async move { Ok(v * 5) }).await;
assert_eq!(res, Err("fail"));
}
#[tokio::test]
async fn test_async_map_or() {
let r: Result<i32, &str> = Ok(2);
let res = r.async_map_or(100, |v| async move { v * 4 }).await;
assert_eq!(res, 8);
let r: Result<i32, &str> = Err("fail");
let res = r.async_map_or(100, |v| async move { v * 4 }).await;
assert_eq!(res, 100);
}
#[tokio::test]
async fn test_async_map_err() {
let r: Result<i32, &str> = Ok(10);
let res = r.async_map_err(|e| async move { e.len() }).await;
assert_eq!(res, Ok(10));
let r: Result<i32, &str> = Err("fail");
let res = r.async_map_err(|e| async move { e.len() }).await;
assert_eq!(res, Err(4));
}
#[tokio::test]
async fn test_async_inspect_err() {
let r: Result<i32, &str> = Err("oops");
let mut seen = "";
let res = r
.async_inspect_err(|e| async {
seen = e;
})
.await;
assert_eq!(res, Err("oops"));
assert_eq!(seen, "oops");
let r: Result<i32, &str> = Ok(10);
let res = r
.async_inspect_err(|e| async {
seen = e;
})
.await;
assert_eq!(res, Ok(10));
}
#[tokio::test]
async fn test_async_map_or_else() {
let r: Result<i32, &str> = Ok(3);
let res = r
.async_map_or_else(|e| async move { e.len() as i32 }, |v| async move { v * 2 })
.await;
assert_eq!(res, 6);
let r: Result<i32, &str> = Err("error");
let res = r
.async_map_or_else(|e| async move { e.len() as i32 }, |v| async move { v * 2 })
.await;
assert_eq!(res, 5);
}
#[tokio::test]
async fn async_is_ok_and() {
let r: Result<i32, &str> = Ok(5);
let res = r.async_is_ok_and(|_| async move { false }).await;
assert!(!res);
let r: Result<i32, &str> = Err("error");
let res = r.async_is_ok_and(|_| async move { false }).await;
assert!(!res);
}
#[tokio::test]
async fn is_err_and() {
let r: Result<i32, &str> = Ok(5);
let res = r.async_is_err_and(|_| async move { true }).await;
assert!(!res);
let r: Result<i32, &str> = Err("error");
let res = r.async_is_err_and(|_| async move { false }).await;
assert!(!res);
}
}