cranpose_services/
network_status.rs1use std::sync::{Arc, OnceLock};
13
14use crate::registry::{RecoveryGate, ServiceRegistry};
15
16#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
18pub enum Connectivity {
19 Online,
21 Offline,
23 #[default]
26 Unknown,
27}
28
29#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
31pub enum Metering {
32 Metered,
34 Unmetered,
36 #[default]
39 Unknown,
40}
41
42#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
44pub struct NetworkStatus {
45 pub connectivity: Connectivity,
46 pub metering: Metering,
47}
48
49pub trait NetworkMonitor: Send + Sync {
51 fn status(&self) -> NetworkStatus;
52 fn is_alive(&self) -> bool;
53 fn reconnect(&self);
54}
55
56pub type NetworkMonitorRef = Arc<dyn NetworkMonitor>;
57
58struct DefaultNetworkMonitor;
59
60impl NetworkMonitor for DefaultNetworkMonitor {
61 fn status(&self) -> NetworkStatus {
62 NetworkStatus::default()
63 }
64
65 fn is_alive(&self) -> bool {
66 true
67 }
68
69 fn reconnect(&self) {}
70}
71
72static PLATFORM_NETWORK_MONITOR: ServiceRegistry<dyn NetworkMonitor> = ServiceRegistry::new();
73static DEFAULT_NETWORK_MONITOR: OnceLock<NetworkMonitorRef> = OnceLock::new();
74static NETWORK_MONITOR_HANDLE: OnceLock<NetworkMonitorRef> = OnceLock::new();
75static NETWORK_RECOVERY: RecoveryGate = RecoveryGate::new();
76
77struct PlatformNetworkMonitor;
78
79fn registered_network_monitor() -> NetworkMonitorRef {
80 PLATFORM_NETWORK_MONITOR
81 .get_or_warn("network monitor")
82 .unwrap_or_else(|| {
83 DEFAULT_NETWORK_MONITOR
84 .get_or_init(|| Arc::new(DefaultNetworkMonitor))
85 .clone()
86 })
87}
88
89fn active_network_monitor() -> NetworkMonitorRef {
90 let monitor = registered_network_monitor();
91 if monitor.is_alive() {
92 NETWORK_RECOVERY.succeeded();
93 } else if NETWORK_RECOVERY.try_start() {
94 monitor.reconnect();
95 }
96 monitor
97}
98
99impl NetworkMonitor for PlatformNetworkMonitor {
100 fn status(&self) -> NetworkStatus {
102 let monitor = active_network_monitor();
103 if monitor.is_alive() {
104 monitor.status()
105 } else {
106 NetworkStatus::default()
107 }
108 }
109
110 fn is_alive(&self) -> bool {
111 registered_network_monitor().is_alive()
112 }
113
114 fn reconnect(&self) {
115 registered_network_monitor().reconnect();
116 }
117}
118
119pub fn set_platform_network_monitor(monitor: NetworkMonitorRef) {
121 PLATFORM_NETWORK_MONITOR.set(monitor);
122 NETWORK_RECOVERY.succeeded();
123}
124
125pub fn clear_platform_network_monitor() {
127 PLATFORM_NETWORK_MONITOR.clear();
128}
129
130pub fn network_monitor() -> NetworkMonitorRef {
133 NETWORK_MONITOR_HANDLE
134 .get_or_init(|| Arc::new(PlatformNetworkMonitor))
135 .clone()
136}
137
138pub fn network_status() -> NetworkStatus {
141 network_monitor().status()
142}
143
144#[cfg(test)]
145#[path = "tests/network_status_tests.rs"]
146mod tests;