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
use std::{
    collections::hash_map::DefaultHasher,
    future::Future,
    hash::{Hash, Hasher},
};

/// Get the current state of network connections as a hash.
pub fn state() -> u64 {
    let mut hash = DefaultHasher::new();

    if let Ok(ifaces) = ifaces::Interface::get_all() {
        for iface in ifaces {
            iface.addr.hash(&mut hash);
        }
    }

    hash.finish()
}

/// Future which exits when the network state has changed.
pub async fn watch_change() {
    let current = state();

    loop {
        tokio::time::sleep(std::time::Duration::from_secs(1)).await;
        let new = state();
        if new != current {
            break;
        }
    }
}

/// Re-attempts a request on network changes.
pub async fn reconnect_on_change<Func, Fut, Res, Retry, Cont>(func: Func, cont: Retry) -> Res
where
    Func: Fn() -> Fut,
    Fut: Future<Output = Res>,
    Retry: Fn() -> Cont,
    Cont: Future<Output = Option<Res>>,
{
    loop {
        let changed = watch_change();
        let future = func();

        futures::pin_mut!(future);
        futures::pin_mut!(changed);

        use futures::future::Either;

        match futures::future::select(future, changed).await {
            Either::Left((result, _)) => break result,
            Either::Right(_) => {
                if let Some(result) = cont().await {
                    break result;
                }
            }
        }
    }
}