tauri_plugin_network_manager/
lib.rs1use commands::{
2 connect_to_wifi, delete_wifi_connection, disconnect_from_wifi, get_network_state,
3 get_saved_wifi_networks, list_wifi_networks, toggle_network_state,
4 get_wireless_enabled, set_wireless_enabled, is_wireless_available,
5 get_network_stats, get_network_interfaces
6};
7pub use models::{NetworkInfo, WiFiConnectionConfig, WiFiSecurityType};
8use serde::{Deserialize, Serialize};
9use std::result::Result;
10use std::sync::{Arc, RwLock};
11use tauri::{
12 plugin::{Builder, TauriPlugin},
13 AppHandle, Emitter, Manager, Runtime,
14};
15
16#[cfg(desktop)]
17pub mod desktop;
18
19mod commands;
20pub mod error;
21pub mod models;
22mod nm_constants;
23mod nm_helpers;
24mod network_stats;
25
26pub use crate::error::{NetworkError, Result as NetworkResult};
27
28pub struct NetworkManagerState<R: Runtime> {
29 pub manager: Arc<RwLock<Option<crate::models::VSKNetworkManager<'static, R>>>>,
30 pub stats_tracker: Arc<RwLock<Option<crate::network_stats::NetworkStatsTracker>>>,
31}
32
33impl<R: Runtime> Default for NetworkManagerState<R> {
34 fn default() -> Self {
35 Self {
36 manager: Arc::new(RwLock::new(None)),
37 stats_tracker: Arc::new(RwLock::new(None)),
38 }
39 }
40}
41
42pub fn spawn_network_change_emitter<R: tauri::Runtime>(
43 app: AppHandle<R>,
44 network_manager: crate::models::VSKNetworkManager<'static, R>,
45) {
46 let rx = match network_manager.listen_network_changes() {
47 Ok(rx) => rx,
48 Err(e) => {
49 eprintln!("No se pudo escuchar cambios de red: {:?}", e);
50 return;
51 }
52 };
53
54 std::thread::spawn(move || {
55 use std::time::{Duration, Instant};
56 use std::sync::mpsc::RecvTimeoutError;
57
58 let mut pending_event: Option<crate::models::NetworkInfo> = None;
59 let mut debounce_deadline: Option<Instant> = None;
60 let debounce_duration = Duration::from_millis(500);
61
62 loop {
63 if let Some(deadline) = debounce_deadline {
64 let now = Instant::now();
65 if now >= deadline {
66 if let Some(info) = pending_event.take() {
68 let _ = app.emit("network-changed", &info);
69 }
70 debounce_deadline = None;
71 } else {
72 let timeout = deadline - now;
74 match rx.recv_timeout(timeout) {
75 Ok(info) => {
76 pending_event = Some(info);
78 debounce_deadline = Some(Instant::now() + debounce_duration);
79 }
80 Err(RecvTimeoutError::Timeout) => {
81 }
83 Err(RecvTimeoutError::Disconnected) => break,
84 }
85 }
86 } else {
87 match rx.recv() {
89 Ok(info) => {
90 pending_event = Some(info);
91 debounce_deadline = Some(Instant::now() + debounce_duration);
98 }
99 Err(_) => break,
100 }
101 }
102 }
103 });
104}
105
106impl<R: Runtime> NetworkManagerState<R> {
107 pub fn new(manager: Option<crate::models::VSKNetworkManager<'static, R>>) -> Self {
108 Self {
109 manager: Arc::new(RwLock::new(manager)),
110 stats_tracker: Arc::new(RwLock::new(None)),
111 }
112 }
113
114 pub fn list_wifi_networks(&self) -> Result<Vec<NetworkInfo>, NetworkError> {
115 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
116 match manager.as_ref() {
117 Some(manager) => manager.list_wifi_networks(),
118 _none => Err(NetworkError::NotInitialized),
119 }
120 }
121
122 pub async fn connect_to_wifi(&self, config: WiFiConnectionConfig) -> Result<(), NetworkError> {
123 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
124 match manager.as_ref() {
125 Some(manager) => manager.connect_to_wifi(config).await,
126 _none => Err(NetworkError::NotInitialized),
127 }
128 }
129
130 pub async fn disconnect_from_wifi(&self) -> Result<(), NetworkError> {
131 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
132 match manager.as_ref() {
133 Some(manager) => manager.disconnect_from_wifi().await,
134 _none => Err(NetworkError::NotInitialized),
135 }
136 }
137
138 pub fn get_saved_wifi_networks(&self) -> Result<Vec<NetworkInfo>, NetworkError> {
139 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
140 match manager.as_ref() {
141 Some(manager) => manager.get_saved_wifi_networks(),
142 _none => Err(NetworkError::NotInitialized),
143 }
144 }
145
146 pub fn delete_wifi_connection(&self, ssid: &str) -> Result<bool, NetworkError> {
147 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
148 match manager.as_ref() {
149 Some(manager) => manager.delete_wifi_connection(ssid),
150 _none => Err(NetworkError::NotInitialized),
151 }
152 }
153
154 pub fn toggle_network_state(&self, enabled: bool) -> Result<bool, NetworkError> {
155 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
156 match manager.as_ref() {
157 Some(manager) => manager.toggle_network_state(enabled),
158 _none => Err(NetworkError::NotInitialized),
159 }
160 }
161
162 pub fn get_wireless_enabled(&self) -> Result<bool, NetworkError> {
163 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
164 match manager.as_ref() {
165 Some(manager) => manager.get_wireless_enabled().map_err(|e| NetworkError::from(e)),
166 _none => Err(NetworkError::NotInitialized),
167 }
168 }
169
170 pub fn set_wireless_enabled(&self, enabled: bool) -> Result<(), NetworkError> {
171 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
172 match manager.as_ref() {
173 Some(manager) => manager.set_wireless_enabled(enabled).map_err(|e| NetworkError::from(e)),
174 _none => Err(NetworkError::NotInitialized),
175 }
176 }
177
178 pub fn is_wireless_available(&self) -> Result<bool, NetworkError> {
179 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
180 match manager.as_ref() {
181 Some(manager) => manager.is_wireless_available().map_err(|e| NetworkError::from(e)),
182 _none => Err(NetworkError::NotInitialized),
183 }
184 }
185
186 pub fn get_network_stats(&self) -> Result<crate::models::NetworkStats, NetworkError> {
187 let mut tracker = self.stats_tracker.write().map_err(|_| NetworkError::LockError)?;
188
189 if tracker.is_none() {
191 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
193 if let Some(manager) = manager.as_ref() {
194 let network_state = manager.get_current_network_state()
195 .map_err(|e| NetworkError::OperationError(e.to_string()))?;
196
197 let interface = if network_state.connection_type == "WiFi" {
199 crate::network_stats::get_network_interfaces()
201 .ok()
202 .and_then(|interfaces| {
203 interfaces.into_iter()
204 .find(|i| i.starts_with("wl") || i.starts_with("wlan"))
205 })
206 .unwrap_or_else(|| "wlan0".to_string())
207 } else {
208 crate::network_stats::get_network_interfaces()
210 .ok()
211 .and_then(|interfaces| {
212 interfaces.into_iter()
213 .find(|i| i.starts_with("en") || i.starts_with("eth"))
214 })
215 .unwrap_or_else(|| "eth0".to_string())
216 };
217
218 *tracker = crate::network_stats::NetworkStatsTracker::new(interface).ok();
219 }
220 }
221
222 match tracker.as_mut() {
224 Some(t) => t.get_stats().map_err(|e| NetworkError::from(e)),
225 None => Err(NetworkError::OperationError("Stats tracker not initialized".to_string())),
226 }
227 }
228}
229
230#[derive(Serialize, Deserialize)]
231struct NetworkRequest {
232 ssid: String,
233 password: Option<String>,
234 security_type: WiFiSecurityType,
235 username: Option<String>,
236}
237
238pub fn init() -> TauriPlugin<tauri::Wry> {
240 Builder::new("network-manager")
241 .invoke_handler(tauri::generate_handler![
242 get_network_state,
243 list_wifi_networks,
244 connect_to_wifi,
245 disconnect_from_wifi,
246 get_saved_wifi_networks,
247 delete_wifi_connection,
248 toggle_network_state,
249 get_wireless_enabled,
250 set_wireless_enabled,
251 is_wireless_available,
252 get_network_stats,
253 get_network_interfaces,
254 ])
255 .setup(|app, _api| -> Result<(), Box<dyn std::error::Error>> {
256 #[cfg(desktop)]
257 let rt = tokio::runtime::Builder::new_multi_thread()
259 .enable_all()
260 .build()?;
261 let network_manager = rt.block_on(async { crate::desktop::init(&app, _api).await })?;
262
263 app.manage(NetworkManagerState::<tauri::Wry>::new(Some(
264 network_manager,
265 )));
266
267 app.state::<NetworkManagerState<tauri::Wry>>()
268 .manager
269 .read()
270 .map_err(|_| NetworkError::LockError)?
271 .as_ref()
272 .map(|manager| {
273 let manager_static: crate::models::VSKNetworkManager<'static, tauri::Wry> =
275 crate::models::VSKNetworkManager {
276 connection: manager.connection.clone(),
277 proxy: manager.proxy.clone(),
278 app: app.clone(),
279 };
280 spawn_network_change_emitter(app.clone(), manager_static);
281 });
282
283 Ok(())
284 })
285 .build()
286}
287
288pub trait NetworkManagerExt<R: Runtime> {
290 fn network_manager(&self) -> Option<crate::models::VSKNetworkManager<'static, R>>;
291}
292
293impl<R: Runtime + Clone, T: Manager<R>> NetworkManagerExt<R> for T {
294 fn network_manager(&self) -> Option<crate::models::VSKNetworkManager<'static, R>> {
295 self.try_state::<NetworkManagerState<R>>()
296 .and_then(|state| {
297 state.manager.read().ok().and_then(|m| {
298 m.as_ref().map(|x| crate::models::VSKNetworkManager {
299 connection: x.connection.clone(),
300 proxy: x.proxy.clone(),
301 app: self.app_handle().clone(),
302 })
303 })
304 })
305 }
306}