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,
48 pub metering: Metering,
49}
50
51pub trait NetworkMonitor: Send + Sync {
53 fn status(&self) -> NetworkStatus;
54 fn is_alive(&self) -> bool;
55 fn reconnect(&self);
56}
57
58pub type NetworkMonitorRef = Arc<dyn NetworkMonitor>;
59
60struct DefaultNetworkMonitor;
61
62impl NetworkMonitor for DefaultNetworkMonitor {
63 fn status(&self) -> NetworkStatus {
64 NetworkStatus::default()
65 }
66
67 fn is_alive(&self) -> bool {
68 true
69 }
70
71 fn reconnect(&self) {}
72}
73
74static PLATFORM_NETWORK_MONITOR: ServiceRegistry<dyn NetworkMonitor> = ServiceRegistry::new();
75static DEFAULT_NETWORK_MONITOR: OnceLock<NetworkMonitorRef> = OnceLock::new();
76static NETWORK_MONITOR_HANDLE: OnceLock<NetworkMonitorRef> = OnceLock::new();
77static NETWORK_RECOVERY: RecoveryGate = RecoveryGate::new();
78
79struct PlatformNetworkMonitor;
80
81fn registered_network_monitor() -> NetworkMonitorRef {
82 PLATFORM_NETWORK_MONITOR
83 .get_or_warn("network monitor")
84 .unwrap_or_else(|| {
85 DEFAULT_NETWORK_MONITOR
86 .get_or_init(|| Arc::new(DefaultNetworkMonitor))
87 .clone()
88 })
89}
90
91fn active_network_monitor() -> NetworkMonitorRef {
92 let monitor = registered_network_monitor();
93 if monitor.is_alive() {
94 NETWORK_RECOVERY.succeeded();
95 } else if NETWORK_RECOVERY.try_start() {
96 monitor.reconnect();
97 }
98 monitor
99}
100
101impl NetworkMonitor for PlatformNetworkMonitor {
102 fn status(&self) -> NetworkStatus {
104 let monitor = active_network_monitor();
105 if monitor.is_alive() {
106 monitor.status()
107 } else {
108 NetworkStatus::default()
109 }
110 }
111
112 fn is_alive(&self) -> bool {
113 registered_network_monitor().is_alive()
114 }
115
116 fn reconnect(&self) {
117 registered_network_monitor().reconnect();
118 }
119}
120
121pub fn set_platform_network_monitor(monitor: NetworkMonitorRef) {
123 PLATFORM_NETWORK_MONITOR.set(monitor);
124 NETWORK_RECOVERY.succeeded();
125}
126
127pub fn clear_platform_network_monitor() {
129 PLATFORM_NETWORK_MONITOR.clear();
130}
131
132pub fn network_monitor() -> NetworkMonitorRef {
135 NETWORK_MONITOR_HANDLE
136 .get_or_init(|| Arc::new(PlatformNetworkMonitor))
137 .clone()
138}
139
140pub fn network_status() -> NetworkStatus {
143 network_monitor().status()
144}
145
146#[cfg(test)]
147#[path = "tests/network_status_tests.rs"]
148mod tests;