async_fetcher/
iface.rs

1use std::{
2    collections::hash_map::DefaultHasher,
3    future::Future,
4    hash::{Hash, Hasher},
5};
6
7/// Get the current state of network connections as a hash.
8pub fn state() -> u64 {
9    let mut hash = DefaultHasher::new();
10
11    if let Ok(ifaces) = ifaces::Interface::get_all() {
12        for iface in ifaces {
13            iface.addr.hash(&mut hash);
14        }
15    }
16
17    hash.finish()
18}
19
20/// Future which exits when the network state has changed.
21pub async fn watch_change() {
22    let current = state();
23
24    loop {
25        tokio::time::sleep(std::time::Duration::from_secs(1)).await;
26        let new = state();
27        if new != current {
28            break;
29        }
30    }
31}
32
33/// Re-attempts a request on network changes.
34pub async fn reconnect_on_change<Func, Fut, Res, Retry, Cont>(func: Func, cont: Retry) -> Res
35where
36    Func: Fn() -> Fut,
37    Fut: Future<Output = Res>,
38    Retry: Fn() -> Cont,
39    Cont: Future<Output = Option<Res>>,
40{
41    loop {
42        let changed = watch_change();
43        let future = func();
44
45        futures::pin_mut!(future);
46        futures::pin_mut!(changed);
47
48        use futures::future::Either;
49
50        match futures::future::select(future, changed).await {
51            Either::Left((result, _)) => break result,
52            Either::Right(_) => {
53                if let Some(result) = cont().await {
54                    break result;
55                }
56            }
57        }
58    }
59}