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};
6pub use models::{NetworkInfo, WiFiConnectionConfig, WiFiSecurityType};
7use serde::{Deserialize, Serialize};
8use std::result::Result;
9use std::sync::{Arc, RwLock};
10use tauri::{
11 plugin::{Builder, TauriPlugin},
12 AppHandle, Emitter, Manager, Runtime,
13};
14
15#[cfg(desktop)]
16pub mod desktop;
17
18mod commands;
19pub mod error;
20pub mod models;
21
22pub use crate::error::{NetworkError, Result as NetworkResult};
23
24#[derive(Default)]
25pub struct NetworkManagerState<R: Runtime> {
26 pub manager: Arc<RwLock<Option<crate::models::VSKNetworkManager<'static, R>>>>,
27}
28
29pub fn spawn_network_change_emitter<R: tauri::Runtime>(
30 app: AppHandle<R>,
31 network_manager: crate::models::VSKNetworkManager<'static, R>,
32) {
33 let rx = match network_manager.listen_network_changes() {
34 Ok(rx) => rx,
35 Err(e) => {
36 eprintln!("No se pudo escuchar cambios de red: {:?}", e);
37 return;
38 }
39 };
40
41 std::thread::spawn(move || {
42 use std::time::{Duration, Instant};
43 use std::sync::mpsc::RecvTimeoutError;
44
45 let mut pending_event: Option<crate::models::NetworkInfo> = None;
46 let mut debounce_deadline: Option<Instant> = None;
47 let debounce_duration = Duration::from_millis(500);
48
49 loop {
50 if let Some(deadline) = debounce_deadline {
51 let now = Instant::now();
52 if now >= deadline {
53 if let Some(info) = pending_event.take() {
55 let _ = app.emit("network-changed", &info);
56 }
57 debounce_deadline = None;
58 } else {
59 let timeout = deadline - now;
61 match rx.recv_timeout(timeout) {
62 Ok(info) => {
63 pending_event = Some(info);
65 debounce_deadline = Some(Instant::now() + debounce_duration);
66 }
67 Err(RecvTimeoutError::Timeout) => {
68 }
70 Err(RecvTimeoutError::Disconnected) => break,
71 }
72 }
73 } else {
74 match rx.recv() {
76 Ok(info) => {
77 pending_event = Some(info);
78 debounce_deadline = Some(Instant::now() + debounce_duration);
85 }
86 Err(_) => break,
87 }
88 }
89 }
90 });
91}
92
93impl<R: Runtime> NetworkManagerState<R> {
94 pub fn new(manager: Option<crate::models::VSKNetworkManager<'static, R>>) -> Self {
95 Self {
96 manager: Arc::new(RwLock::new(manager)),
97 }
98 }
99
100 pub fn list_wifi_networks(&self) -> Result<Vec<NetworkInfo>, NetworkError> {
101 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
102 match manager.as_ref() {
103 Some(manager) => manager.list_wifi_networks(),
104 _none => Err(NetworkError::NotInitialized),
105 }
106 }
107
108 pub async fn connect_to_wifi(&self, config: WiFiConnectionConfig) -> Result<(), NetworkError> {
109 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
110 match manager.as_ref() {
111 Some(manager) => manager.connect_to_wifi(config).await,
112 _none => Err(NetworkError::NotInitialized),
113 }
114 }
115
116 pub async fn disconnect_from_wifi(&self) -> Result<(), NetworkError> {
117 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
118 match manager.as_ref() {
119 Some(manager) => manager.disconnect_from_wifi().await,
120 _none => Err(NetworkError::NotInitialized),
121 }
122 }
123
124 pub fn get_saved_wifi_networks(&self) -> Result<Vec<NetworkInfo>, NetworkError> {
125 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
126 match manager.as_ref() {
127 Some(manager) => manager.get_saved_wifi_networks(),
128 _none => Err(NetworkError::NotInitialized),
129 }
130 }
131
132 pub fn delete_wifi_connection(&self, ssid: &str) -> Result<bool, NetworkError> {
133 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
134 match manager.as_ref() {
135 Some(manager) => manager.delete_wifi_connection(ssid),
136 _none => Err(NetworkError::NotInitialized),
137 }
138 }
139
140 pub fn toggle_network_state(&self, enabled: bool) -> Result<bool, NetworkError> {
141 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
142 match manager.as_ref() {
143 Some(manager) => manager.toggle_network_state(enabled),
144 _none => Err(NetworkError::NotInitialized),
145 }
146 }
147
148 pub fn get_wireless_enabled(&self) -> Result<bool, NetworkError> {
149 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
150 match manager.as_ref() {
151 Some(manager) => manager.get_wireless_enabled().map_err(|e| NetworkError::from(e)),
152 _none => Err(NetworkError::NotInitialized),
153 }
154 }
155
156 pub fn set_wireless_enabled(&self, enabled: bool) -> Result<(), NetworkError> {
157 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
158 match manager.as_ref() {
159 Some(manager) => manager.set_wireless_enabled(enabled).map_err(|e| NetworkError::from(e)),
160 _none => Err(NetworkError::NotInitialized),
161 }
162 }
163
164 pub fn is_wireless_available(&self) -> Result<bool, NetworkError> {
165 let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
166 match manager.as_ref() {
167 Some(manager) => manager.is_wireless_available().map_err(|e| NetworkError::from(e)),
168 _none => Err(NetworkError::NotInitialized),
169 }
170 }
171}
172
173#[derive(Serialize, Deserialize)]
174struct NetworkRequest {
175 ssid: String,
176 password: Option<String>,
177 security_type: WiFiSecurityType,
178 username: Option<String>,
179}
180
181pub fn init() -> TauriPlugin<tauri::Wry> {
183 Builder::new("network-manager")
184 .invoke_handler(tauri::generate_handler![
185 get_network_state,
186 list_wifi_networks,
187 connect_to_wifi,
188 disconnect_from_wifi,
189 get_saved_wifi_networks,
190 delete_wifi_connection,
191 toggle_network_state,
192 get_wireless_enabled,
193 set_wireless_enabled,
194 is_wireless_available,
195 ])
196 .setup(|app, _api| -> Result<(), Box<dyn std::error::Error>> {
197 #[cfg(desktop)]
198 let rt = tokio::runtime::Builder::new_multi_thread()
200 .enable_all()
201 .build()?;
202 let network_manager = rt.block_on(async { crate::desktop::init(&app, _api).await })?;
203
204 app.manage(NetworkManagerState::<tauri::Wry>::new(Some(
205 network_manager,
206 )));
207
208 app.state::<NetworkManagerState<tauri::Wry>>()
209 .manager
210 .read()
211 .map_err(|_| NetworkError::LockError)?
212 .as_ref()
213 .map(|manager| {
214 let manager_static: crate::models::VSKNetworkManager<'static, tauri::Wry> =
216 crate::models::VSKNetworkManager {
217 connection: manager.connection.clone(),
218 proxy: manager.proxy.clone(),
219 app: app.clone(),
220 };
221 spawn_network_change_emitter(app.clone(), manager_static);
222 });
223
224 Ok(())
225 })
226 .build()
227}
228
229pub trait NetworkManagerExt<R: Runtime> {
231 fn network_manager(&self) -> Option<crate::models::VSKNetworkManager<'static, R>>;
232}
233
234impl<R: Runtime + Clone, T: Manager<R>> NetworkManagerExt<R> for T {
235 fn network_manager(&self) -> Option<crate::models::VSKNetworkManager<'static, R>> {
236 self.try_state::<NetworkManagerState<R>>()
237 .and_then(|state| {
238 state.manager.read().ok().and_then(|m| {
239 m.as_ref().map(|x| crate::models::VSKNetworkManager {
240 connection: x.connection.clone(),
241 proxy: x.proxy.clone(),
242 app: self.app_handle().clone(),
243 })
244 })
245 })
246 }
247}