diff --git a/varManager_backend/src/api/mod.rs b/varManager_backend/src/api/mod.rs index cbff1bf..6d47c21 100644 --- a/varManager_backend/src/api/mod.rs +++ b/varManager_backend/src/api/mod.rs @@ -1893,6 +1893,7 @@ pub async fn list_packswitch( let addon_path = crate::infra::paths::addon_packages_dir(&vampath); let link_root = addon_path.join(crate::infra::paths::INSTALL_LINK_DIR); + let switch_root = crate::infra::paths::addon_switch_root(&vampath); let current = if let Ok(target) = crate::infra::winfs::read_link_target(&link_root) { let resolved = if target.is_absolute() { target @@ -1902,7 +1903,6 @@ pub async fn list_packswitch( .unwrap_or(&addon_path) .join(target) }; - let switch_root = crate::infra::paths::addon_switch_root(&vampath); if resolved.starts_with(&switch_root) { resolved .parent() @@ -1913,6 +1913,24 @@ pub async fn list_packswitch( } else { "default".to_string() } + } else if let Ok(target) = crate::infra::winfs::read_link_target(&addon_path) { + let resolved = if target.is_absolute() { + target + } else { + addon_path + .parent() + .unwrap_or(&addon_path) + .join(target) + }; + if resolved.starts_with(&switch_root) { + resolved + .file_name() + .and_then(|s| s.to_str()) + .unwrap_or("default") + .to_string() + } else { + "default".to_string() + } } else { "default".to_string() }; diff --git a/varManager_backend/src/jobs/packswitch.rs b/varManager_backend/src/jobs/packswitch.rs index bf82312..d9a199f 100644 --- a/varManager_backend/src/jobs/packswitch.rs +++ b/varManager_backend/src/jobs/packswitch.rs @@ -212,10 +212,10 @@ fn set_switch_blocking( fs::create_dir_all(&target).map_err(|err| err.to_string())?; let addon_path = addon_packages_dir(&vampath); - fs::create_dir_all(&addon_path).map_err(|err| err.to_string())?; + let addon_was_symlink = ensure_addonpackages_dir(&addon_path, reporter)?; let managed_dirs = collect_managed_dirs(); - if managed_dirs_have_real_vars(&addon_path, &managed_dirs) { + if !addon_was_symlink && managed_dirs_have_real_vars(&addon_path, &managed_dirs) { reporter.log(format!( "Managed link folders contain real var files; update DB required: {}", addon_path.display() @@ -252,6 +252,28 @@ fn collect_managed_dirs() -> BTreeSet { dirs } +fn ensure_addonpackages_dir(addon_path: &Path, reporter: &JobReporter) -> Result { + if fs_util::is_symlink(addon_path) { + reporter.log(format!( + "AddonPackages is a symlink; migrating to folder-based packswitch: {}", + addon_path.display() + )); + if fs::remove_file(addon_path).is_err() { + fs::remove_dir_all(addon_path).map_err(|err| err.to_string())?; + } + fs::create_dir_all(addon_path).map_err(|err| err.to_string())?; + return Ok(true); + } + if addon_path.exists() && !addon_path.is_dir() { + return Err(format!( + "AddonPackages is not a directory: {}", + addon_path.display() + )); + } + fs::create_dir_all(addon_path).map_err(|err| err.to_string())?; + Ok(false) +} + fn managed_dirs_have_real_vars(addon_path: &Path, managed_dirs: &BTreeSet) -> bool { for dir_name in managed_dirs { let path = addon_path.join(dir_name); @@ -601,4 +623,26 @@ mod tests { let _ = fs::remove_dir_all(&root); } + + #[test] + fn migrate_addonpackages_symlink_to_directory() { + if !symlink_supported() { + return; + } + let root = make_temp_dir("packswitch_migrate_symlink"); + let addon_path = root.join("AddonPackages"); + let target = root.join("legacy_target"); + fs::create_dir_all(&target).unwrap(); + winfs::create_symlink_dir(&addon_path, &target).unwrap(); + + let (tx, _rx) = create_job_channel(); + let reporter = JobReporter::new(1, tx); + let was_symlink = ensure_addonpackages_dir(&addon_path, &reporter).unwrap(); + + assert!(was_symlink); + assert!(addon_path.exists()); + assert!(!fs_util::is_symlink(&addon_path)); + + let _ = fs::remove_dir_all(&root); + } } diff --git a/varmanager_flutter/lib/features/home/home_page.dart b/varmanager_flutter/lib/features/home/home_page.dart index a212293..47bfd15 100644 --- a/varmanager_flutter/lib/features/home/home_page.dart +++ b/varmanager_flutter/lib/features/home/home_page.dart @@ -731,6 +731,7 @@ class _HomePageState extends ConsumerState { final result = await _runJob('update_db'); ref.invalidate(varsListProvider); await _showUpdateDbSummary(result); + await _loadPackSwitches(); }, icon: const Icon(Icons.sync), label: Text(l10n.updateDbLabel),