Skip to main content

cranpose_services/
network_status.rs

1//! Network status (connectivity + whether the connection is metered). Apps use
2//! it to defer or confirm large transfers on cellular/metered links.
3//!
4//! The default reports online + unmetered; platform backends can install a real
5//! monitor via [`set_platform_network_monitor`] (iOS `NWPathMonitor`, Android
6//! `ConnectivityManager`, web `navigator.connection`).
7
8use std::cell::RefCell;
9use std::rc::Rc;
10
11/// A snapshot of the current network state.
12#[derive(Clone, Copy, PartialEq, Eq, Debug)]
13pub struct NetworkStatus {
14    /// Whether the device currently has a usable network path.
15    pub online: bool,
16    /// Whether the active path is metered/expensive (cellular, hotspot, …).
17    pub metered: bool,
18}
19
20impl Default for NetworkStatus {
21    fn default() -> Self {
22        Self {
23            online: true,
24            metered: false,
25        }
26    }
27}
28
29/// Reports the current network status.
30pub trait NetworkMonitor {
31    fn status(&self) -> NetworkStatus;
32}
33
34pub type NetworkMonitorRef = Rc<dyn NetworkMonitor>;
35
36struct DefaultNetworkMonitor;
37
38impl NetworkMonitor for DefaultNetworkMonitor {
39    fn status(&self) -> NetworkStatus {
40        NetworkStatus::default()
41    }
42}
43
44thread_local! {
45    static PLATFORM_NETWORK_MONITOR: RefCell<Option<NetworkMonitorRef>> = const { RefCell::new(None) };
46}
47
48/// Installs a platform network monitor, replacing any previous one.
49pub fn set_platform_network_monitor(monitor: NetworkMonitorRef) {
50    PLATFORM_NETWORK_MONITOR.with(|cell| *cell.borrow_mut() = Some(monitor));
51}
52
53/// Removes any registered platform network monitor (tests and teardown).
54pub fn clear_platform_network_monitor() {
55    PLATFORM_NETWORK_MONITOR.with(|cell| *cell.borrow_mut() = None);
56}
57
58/// The active network monitor: the platform one if installed, else the default
59/// (online, unmetered).
60pub fn network_monitor() -> NetworkMonitorRef {
61    PLATFORM_NETWORK_MONITOR
62        .with(|cell| cell.borrow().clone())
63        .unwrap_or_else(|| Rc::new(DefaultNetworkMonitor))
64}
65
66/// Convenience: the current network status.
67pub fn network_status() -> NetworkStatus {
68    network_monitor().status()
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn default_is_online_unmetered_and_overridable() {
77        clear_platform_network_monitor();
78        assert_eq!(
79            network_status(),
80            NetworkStatus {
81                online: true,
82                metered: false
83            }
84        );
85        struct Metered;
86        impl NetworkMonitor for Metered {
87            fn status(&self) -> NetworkStatus {
88                NetworkStatus {
89                    online: true,
90                    metered: true,
91                }
92            }
93        }
94        set_platform_network_monitor(Rc::new(Metered));
95        assert!(network_status().metered);
96        clear_platform_network_monitor();
97    }
98}