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 Arc::clone(DEFAULT_NETWORK_MONITOR.get_or_init(|| Arc::new(DefaultNetworkMonitor)))
92 })
93}
94
95fn active_network_monitor() -> NetworkMonitorRef {
96 let monitor = registered_network_monitor();
97 if monitor.is_alive() {
98 NETWORK_RECOVERY.succeeded();
99 } else if NETWORK_RECOVERY.try_start() {
100 monitor.reconnect();
101 }
102 monitor
103}
104
105impl NetworkMonitor for PlatformNetworkMonitor {
106 fn status(&self) -> NetworkStatus {
108 let monitor = active_network_monitor();
109 if monitor.is_alive() {
110 monitor.status()
111 } else {
112 NetworkStatus::default()
113 }
114 }
115
116 fn is_alive(&self) -> bool {
117 registered_network_monitor().is_alive()
118 }
119
120 fn reconnect(&self) {
121 registered_network_monitor().reconnect();
122 }
123}
124
125pub fn set_platform_network_monitor(monitor: NetworkMonitorRef) {
127 PLATFORM_NETWORK_MONITOR.set(monitor);
128 NETWORK_RECOVERY.succeeded();
129}
130
131pub fn clear_platform_network_monitor() {
133 PLATFORM_NETWORK_MONITOR.clear();
134}
135
136pub fn network_monitor() -> NetworkMonitorRef {
139 Arc::clone(NETWORK_MONITOR_HANDLE.get_or_init(|| Arc::new(PlatformNetworkMonitor)))
140}
141
142pub fn network_status() -> NetworkStatus {
145 network_monitor().status()
146}
147
148#[cfg(test)]
149#[path = "tests/network_status_tests.rs"]
150mod tests;