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