1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
use crate::call_option::{RetrySettings, Retryer};
use google_cloud_googleapis::Status;
use std::future::Future;

pub trait TryAs<T> {
    fn try_as(&self) -> Result<&T, ()>;
}

impl TryAs<Status> for Status {
    fn try_as(&self) -> Result<&Status, ()> {
        Ok(self)
    }
}

/// Repeats retries when the specified error is detected.
pub async fn invoke<Setting, T, E, Fut>(
    mut f: impl FnMut() -> Fut,
    settings: &mut RetrySettings<Setting>,
) -> Result<T, E>
where
    E: TryAs<Status>,
    Fut: Future<Output = Result<T, E>>,
    Setting: Retryer + Clone,
{
    let retryer = &mut settings.retryer;
    loop {
        let err = match f().await {
            Ok(s) => return Ok(s),
            Err(e) => e,
        };

        let status = match err.try_as() {
            Ok(s) => s,
            _ => return Err(err),
        };

        match retryer.retry(status) {
            Some(duration) => tokio::time::sleep(duration).await,
            None => return Err(err),
        };
    }
}

/// Repeats retries when the specified error is detected.
/// The argument specified by 'v' can be reused for each retry.
pub async fn invoke_reuse<Setting, T, E, V, Fut>(
    mut f: impl FnMut(V) -> Fut,
    mut v: V,
    settings: &mut RetrySettings<Setting>,
) -> Result<T, E>
where
    E: TryAs<Status>,
    Fut: Future<Output = Result<T, (E, V)>>,
    Setting: Retryer + Clone,
{
    let retryer = &mut settings.retryer;
    loop {
        let result = f(v).await;
        let err = match result {
            Ok(s) => return Ok(s),
            Err(e) => {
                v = e.1;
                e.0
            }
        };
        let status = match err.try_as() {
            Ok(s) => s,
            _ => return Err(err),
        };
        match retryer.retry(status) {
            Some(duration) => tokio::time::sleep(duration).await,
            None => return Err(err),
        };
    }
}