use commands::{
connect_to_wifi, delete_wifi_connection, disconnect_from_wifi, get_network_state,
get_saved_wifi_networks, list_wifi_networks, toggle_network_state,
get_wireless_enabled, set_wireless_enabled, is_wireless_available,
get_network_stats, get_network_interfaces
};
pub use models::{NetworkInfo, WiFiConnectionConfig, WiFiSecurityType};
use serde::{Deserialize, Serialize};
use std::result::Result;
use std::sync::{Arc, RwLock};
use tauri::{
plugin::{Builder, TauriPlugin},
AppHandle, Emitter, Manager, Runtime,
};
#[cfg(desktop)]
pub mod desktop;
mod commands;
pub mod error;
pub mod models;
mod nm_constants;
mod nm_helpers;
mod network_stats;
pub use crate::error::{NetworkError, Result as NetworkResult};
pub struct NetworkManagerState<R: Runtime> {
pub manager: Arc<RwLock<Option<crate::models::VSKNetworkManager<'static, R>>>>,
pub stats_tracker: Arc<RwLock<Option<crate::network_stats::NetworkStatsTracker>>>,
}
impl<R: Runtime> Default for NetworkManagerState<R> {
fn default() -> Self {
Self {
manager: Arc::new(RwLock::new(None)),
stats_tracker: Arc::new(RwLock::new(None)),
}
}
}
pub fn spawn_network_change_emitter<R: tauri::Runtime>(
app: AppHandle<R>,
network_manager: crate::models::VSKNetworkManager<'static, R>,
) {
let rx = match network_manager.listen_network_changes() {
Ok(rx) => rx,
Err(e) => {
eprintln!("No se pudo escuchar cambios de red: {:?}", e);
return;
}
};
std::thread::spawn(move || {
use std::time::{Duration, Instant};
use std::sync::mpsc::RecvTimeoutError;
let mut pending_event: Option<crate::models::NetworkInfo> = None;
let mut debounce_deadline: Option<Instant> = None;
let debounce_duration = Duration::from_millis(500);
loop {
if let Some(deadline) = debounce_deadline {
let now = Instant::now();
if now >= deadline {
if let Some(info) = pending_event.take() {
let _ = app.emit("network-changed", &info);
}
debounce_deadline = None;
} else {
let timeout = deadline - now;
match rx.recv_timeout(timeout) {
Ok(info) => {
pending_event = Some(info);
debounce_deadline = Some(Instant::now() + debounce_duration);
}
Err(RecvTimeoutError::Timeout) => {
}
Err(RecvTimeoutError::Disconnected) => break,
}
}
} else {
match rx.recv() {
Ok(info) => {
pending_event = Some(info);
debounce_deadline = Some(Instant::now() + debounce_duration);
}
Err(_) => break,
}
}
}
});
}
impl<R: Runtime> NetworkManagerState<R> {
pub fn new(manager: Option<crate::models::VSKNetworkManager<'static, R>>) -> Self {
Self {
manager: Arc::new(RwLock::new(manager)),
stats_tracker: Arc::new(RwLock::new(None)),
}
}
pub fn list_wifi_networks(&self) -> Result<Vec<NetworkInfo>, NetworkError> {
let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
match manager.as_ref() {
Some(manager) => manager.list_wifi_networks(),
_none => Err(NetworkError::NotInitialized),
}
}
pub async fn connect_to_wifi(&self, config: WiFiConnectionConfig) -> Result<(), NetworkError> {
let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
match manager.as_ref() {
Some(manager) => manager.connect_to_wifi(config).await,
_none => Err(NetworkError::NotInitialized),
}
}
pub async fn disconnect_from_wifi(&self) -> Result<(), NetworkError> {
let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
match manager.as_ref() {
Some(manager) => manager.disconnect_from_wifi().await,
_none => Err(NetworkError::NotInitialized),
}
}
pub fn get_saved_wifi_networks(&self) -> Result<Vec<NetworkInfo>, NetworkError> {
let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
match manager.as_ref() {
Some(manager) => manager.get_saved_wifi_networks(),
_none => Err(NetworkError::NotInitialized),
}
}
pub fn delete_wifi_connection(&self, ssid: &str) -> Result<bool, NetworkError> {
let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
match manager.as_ref() {
Some(manager) => manager.delete_wifi_connection(ssid),
_none => Err(NetworkError::NotInitialized),
}
}
pub fn toggle_network_state(&self, enabled: bool) -> Result<bool, NetworkError> {
let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
match manager.as_ref() {
Some(manager) => manager.toggle_network_state(enabled),
_none => Err(NetworkError::NotInitialized),
}
}
pub fn get_wireless_enabled(&self) -> Result<bool, NetworkError> {
let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
match manager.as_ref() {
Some(manager) => manager.get_wireless_enabled().map_err(|e| NetworkError::from(e)),
_none => Err(NetworkError::NotInitialized),
}
}
pub fn set_wireless_enabled(&self, enabled: bool) -> Result<(), NetworkError> {
let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
match manager.as_ref() {
Some(manager) => manager.set_wireless_enabled(enabled).map_err(|e| NetworkError::from(e)),
_none => Err(NetworkError::NotInitialized),
}
}
pub fn is_wireless_available(&self) -> Result<bool, NetworkError> {
let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
match manager.as_ref() {
Some(manager) => manager.is_wireless_available().map_err(|e| NetworkError::from(e)),
_none => Err(NetworkError::NotInitialized),
}
}
pub fn get_network_stats(&self) -> Result<crate::models::NetworkStats, NetworkError> {
let mut tracker = self.stats_tracker.write().map_err(|_| NetworkError::LockError)?;
if tracker.is_none() {
let manager = self.manager.read().map_err(|_| NetworkError::LockError)?;
if let Some(manager) = manager.as_ref() {
let network_state = manager.get_current_network_state()
.map_err(|e| NetworkError::OperationError(e.to_string()))?;
let interface = if network_state.connection_type == "WiFi" {
crate::network_stats::get_network_interfaces()
.ok()
.and_then(|interfaces| {
interfaces.into_iter()
.find(|i| i.starts_with("wl") || i.starts_with("wlan"))
})
.unwrap_or_else(|| "wlan0".to_string())
} else {
crate::network_stats::get_network_interfaces()
.ok()
.and_then(|interfaces| {
interfaces.into_iter()
.find(|i| i.starts_with("en") || i.starts_with("eth"))
})
.unwrap_or_else(|| "eth0".to_string())
};
*tracker = crate::network_stats::NetworkStatsTracker::new(interface).ok();
}
}
match tracker.as_mut() {
Some(t) => t.get_stats().map_err(|e| NetworkError::from(e)),
None => Err(NetworkError::OperationError("Stats tracker not initialized".to_string())),
}
}
}
#[derive(Serialize, Deserialize)]
struct NetworkRequest {
ssid: String,
password: Option<String>,
security_type: WiFiSecurityType,
username: Option<String>,
}
pub fn init() -> TauriPlugin<tauri::Wry> {
Builder::new("network-manager")
.invoke_handler(tauri::generate_handler![
get_network_state,
list_wifi_networks,
connect_to_wifi,
disconnect_from_wifi,
get_saved_wifi_networks,
delete_wifi_connection,
toggle_network_state,
get_wireless_enabled,
set_wireless_enabled,
is_wireless_available,
get_network_stats,
get_network_interfaces,
])
.setup(|app, _api| -> Result<(), Box<dyn std::error::Error>> {
#[cfg(desktop)]
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?;
let network_manager = rt.block_on(async { crate::desktop::init(&app, _api).await })?;
app.manage(NetworkManagerState::<tauri::Wry>::new(Some(
network_manager,
)));
app.state::<NetworkManagerState<tauri::Wry>>()
.manager
.read()
.map_err(|_| NetworkError::LockError)?
.as_ref()
.map(|manager| {
let manager_static: crate::models::VSKNetworkManager<'static, tauri::Wry> =
crate::models::VSKNetworkManager {
connection: manager.connection.clone(),
proxy: manager.proxy.clone(),
app: app.clone(),
};
spawn_network_change_emitter(app.clone(), manager_static);
});
Ok(())
})
.build()
}
pub trait NetworkManagerExt<R: Runtime> {
fn network_manager(&self) -> Option<crate::models::VSKNetworkManager<'static, R>>;
}
impl<R: Runtime + Clone, T: Manager<R>> NetworkManagerExt<R> for T {
fn network_manager(&self) -> Option<crate::models::VSKNetworkManager<'static, R>> {
self.try_state::<NetworkManagerState<R>>()
.and_then(|state| {
state.manager.read().ok().and_then(|m| {
m.as_ref().map(|x| crate::models::VSKNetworkManager {
connection: x.connection.clone(),
proxy: x.proxy.clone(),
app: self.app_handle().clone(),
})
})
})
}
}