use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use serde::Deserialize; use tauri::AppHandle; use crate::net::range::parse_ip_range; use crate::scanner::ping::ping_sweep; #[derive(Debug, Clone, Deserialize)] pub struct ScanConfig { pub range: String, pub timeout_ms: Option, pub port_scan: Option, pub ports: Option>, pub port_timeout_ms: Option, } static CANCEL_FLAG: std::sync::OnceLock> = std::sync::OnceLock::new(); fn get_cancel_flag() -> Arc { CANCEL_FLAG .get_or_init(|| Arc::new(AtomicBool::new(false))) .clone() } #[tauri::command] pub async fn start_scan(app: AppHandle, config: ScanConfig) -> Result<(), String> { let ips = parse_ip_range(&config.range).map_err(|e| format!("Invalid IP range: {}", e))?; let timeout_ms = config.timeout_ms.unwrap_or(1500); let cancel = get_cancel_flag(); // Reset cancel flag from any previous scan cancel.store(false, Ordering::Relaxed); // Spawn the scan as a background task so this command returns immediately let port_scan = config.port_scan.unwrap_or(false); let ports = config.ports.clone(); let port_timeout_ms = config.port_timeout_ms.unwrap_or(1000); tokio::spawn(async move { ping_sweep(&app, &ips, timeout_ms, cancel, port_scan, ports, port_timeout_ms).await; }); Ok(()) } #[tauri::command] pub async fn stop_scan() -> Result<(), String> { let cancel = get_cancel_flag(); cancel.store(true, Ordering::Relaxed); tracing::info!("Scan cancelled"); Ok(()) }