diff --git a/src-tauri/src/cmd/mod.rs b/src-tauri/src/cmd/mod.rs index 6c748687..3b5e8758 100644 --- a/src-tauri/src/cmd/mod.rs +++ b/src-tauri/src/cmd/mod.rs @@ -11,6 +11,7 @@ pub mod lightweight; pub mod media_unlock_checker; pub mod network; pub mod profile; +mod profile_switch; pub mod proxy; pub mod runtime; pub mod save_profile; diff --git a/src-tauri/src/cmd/profile.rs b/src-tauri/src/cmd/profile.rs index e64f2148..838985d8 100644 --- a/src-tauri/src/cmd/profile.rs +++ b/src-tauri/src/cmd/profile.rs @@ -1,5 +1,6 @@ use super::CmdResult; use super::StringifyErr; +use super::profile_switch::validate_switch_request; use crate::{ config::{ Config, IProfiles, PrfItem, PrfOption, @@ -17,12 +18,11 @@ use crate::{ }; use futures::FutureExt; use once_cell::sync::OnceCell; -use serde_yaml_ng as serde_yaml; + use smartstring::alias::String; use std::{ any::Any, collections::VecDeque, - fs, panic::AssertUnwindSafe, sync::atomic::{AtomicBool, AtomicU64, Ordering}, time::Duration, @@ -33,73 +33,6 @@ use tokio::sync::{ oneshot, }; -async fn validate_switch_request(task_id: u64, profile_id: &str) -> Result<(), String> { - logging!( - info, - Type::Cmd, - "Validating profile switch task {} -> {}", - task_id, - profile_id - ); - - let profile_key: String = profile_id.into(); - let (file_path, profile_type, is_current, remote_url) = { - let profiles_guard = Config::profiles().await; - let latest = profiles_guard.latest_ref(); - let item = latest.get_item(&profile_key).map_err(|err| -> String { - format!("Target profile {} not found: {}", profile_id, err).into() - })?; - ( - item.file.clone().map(|f| f.to_string()), - item.itype.clone().map(|t| t.to_string()), - latest - .current - .as_ref() - .map(|current| current.as_str() == profile_id) - .unwrap_or(false), - item.url.clone().map(|u| u.to_string()), - ) - }; - - if is_current { - logging!( - info, - Type::Cmd, - "Switch task {} is targeting the current profile {}; skipping validation", - task_id, - profile_id - ); - return Ok(()); - } - - if matches!(profile_type.as_deref(), Some("remote")) { - let has_url = remote_url.as_ref().map(|u| !u.is_empty()).unwrap_or(false); - if !has_url { - return Err({ - let msg = format!("Remote profile {} is missing a download URL", profile_id); - msg.into() - }); - } - } - - if let Some(file) = file_path { - let profiles_dir = dirs::app_profiles_dir().map_err(|err| -> String { - format!("Failed to resolve profiles directory: {}", err).into() - })?; - let path = profiles_dir.join(&file); - - let contents = fs::read_to_string(&path).map_err(|err| -> String { - format!("Failed to read profile file {}: {}", path.display(), err).into() - })?; - - serde_yaml::from_str::(&contents).map_err(|err| -> String { - format!("Profile YAML parse failed for {}: {}", path.display(), err).into() - })?; - } - - Ok(()) -} - static SWITCH_MUTEX: OnceCell> = OnceCell::new(); static SWITCH_QUEUE: OnceCell> = OnceCell::new(); const SWITCH_QUEUE_CAPACITY: usize = 32; diff --git a/src-tauri/src/cmd/profile_switch.rs b/src-tauri/src/cmd/profile_switch.rs new file mode 100644 index 00000000..a1219274 --- /dev/null +++ b/src-tauri/src/cmd/profile_switch.rs @@ -0,0 +1,76 @@ +use crate::{ + config::Config, + logging, + utils::{dirs, logging::Type}, +}; +use serde_yaml_ng as serde_yaml; +use smartstring::alias::String; +use std::fs; + +/// Validate profile switch request before queueing. +pub async fn validate_switch_request(task_id: u64, profile_id: &str) -> Result<(), String> { + logging!( + info, + Type::Cmd, + "Validating profile switch task {} -> {}", + task_id, + profile_id + ); + + let profile_key: String = profile_id.into(); + let (file_path, profile_type, is_current, remote_url) = { + let profiles_guard = Config::profiles().await; + let latest = profiles_guard.latest_ref(); + let item = latest.get_item(&profile_key).map_err(|err| -> String { + format!("Target profile {} not found: {}", profile_id, err).into() + })?; + ( + item.file.clone().map(|f| f.to_string()), + item.itype.clone().map(|t| t.to_string()), + latest + .current + .as_ref() + .map(|current| current.as_str() == profile_id) + .unwrap_or(false), + item.url.clone().map(|u| u.to_string()), + ) + }; + + if is_current { + logging!( + info, + Type::Cmd, + "Switch task {} is targeting the current profile {}; skipping validation", + task_id, + profile_id + ); + return Ok(()); + } + + if matches!(profile_type.as_deref(), Some("remote")) { + let has_url = remote_url.as_ref().map(|u| !u.is_empty()).unwrap_or(false); + if !has_url { + return Err({ + let msg = format!("Remote profile {} is missing a download URL", profile_id); + msg.into() + }); + } + } + + if let Some(file) = file_path { + let profiles_dir = dirs::app_profiles_dir().map_err(|err| -> String { + format!("Failed to resolve profiles directory: {}", err).into() + })?; + let path = profiles_dir.join(&file); + + let contents = fs::read_to_string(&path).map_err(|err| -> String { + format!("Failed to read profile file {}: {}", path.display(), err).into() + })?; + + serde_yaml::from_str::(&contents).map_err(|err| -> String { + format!("Profile YAML parse failed for {}: {}", path.display(), err).into() + })?; + } + + Ok(()) +}