use crate::changeset::ChangesetManager;
use crate::config::{PackageToolsConfig, UpgradeConfig};
use crate::error::{UpgradeError, UpgradeResult};
use crate::upgrade::application::{apply_upgrades, apply_with_changeset};
use crate::upgrade::backup::BackupManager;
use crate::upgrade::detection::{DetectionOptions, UpgradePreview, detect_upgrades};
use crate::upgrade::registry::RegistryClient;
use crate::upgrade::{UpgradeResult as UpgradeResultType, UpgradeSelection};
use std::path::PathBuf;
use sublime_standard_tools::filesystem::FileSystemManager;
pub struct UpgradeManager {
workspace_root: PathBuf,
config: UpgradeConfig,
registry_client: RegistryClient,
backup_manager: BackupManager<FileSystemManager>,
fs: FileSystemManager,
last_backup_id: Option<String>,
}
impl UpgradeManager {
pub async fn new(workspace_root: PathBuf, config: UpgradeConfig) -> UpgradeResult<Self> {
let fs = FileSystemManager::new();
let registry_client = RegistryClient::new(&workspace_root, config.registry.clone()).await?;
let backup_manager =
BackupManager::new(workspace_root.clone(), config.backup.clone(), fs.clone());
Ok(Self {
workspace_root,
config,
registry_client,
backup_manager,
fs,
last_backup_id: None,
})
}
pub async fn detect_upgrades(
&self,
options: DetectionOptions,
) -> UpgradeResult<UpgradePreview> {
detect_upgrades(&self.workspace_root, &self.registry_client, &self.fs, options).await
}
pub async fn apply_upgrades(
&mut self,
selection: UpgradeSelection,
dry_run: bool,
backup_enabled: Option<bool>,
) -> UpgradeResult<UpgradeResultType> {
let detection_options = self.selection_to_detection_options(&selection);
let preview = self.detect_upgrades(detection_options).await?;
if preview.packages.is_empty() {
return apply_upgrades(vec![], selection, dry_run, &self.fs).await;
}
if dry_run {
return apply_upgrades(preview.packages, selection, dry_run, &self.fs).await;
}
let should_backup = backup_enabled.unwrap_or(self.config.backup.enabled);
let backup_id = if should_backup {
let files_to_backup = self.collect_package_json_files(&preview.packages)?;
let backup_id = self.backup_manager.create_backup(&files_to_backup, "upgrade").await?;
Some(backup_id)
} else {
None
};
let result = if self.config.auto_changeset {
let pkg_config =
PackageToolsConfig { upgrade: self.config.clone(), ..Default::default() };
let changeset_manager =
ChangesetManager::new(&self.workspace_root, self.fs.clone(), pkg_config)
.await
.map_err(|e| UpgradeError::ChangesetCreationFailed {
reason: format!("Failed to initialize changeset manager: {}", e.as_ref()),
})?;
apply_with_changeset(
preview.packages,
selection,
dry_run,
&self.workspace_root,
&self.config,
Some(&changeset_manager),
&self.fs,
)
.await
} else {
apply_upgrades(preview.packages, selection, dry_run, &self.fs).await
};
match result {
Ok(upgrade_result) => {
if let Some(id) = backup_id.clone() {
self.last_backup_id = Some(id.clone());
}
if should_backup
&& !self.config.backup.keep_after_success
&& let Some(id) = backup_id
{
let _ = self.backup_manager.delete_backup(&id).await;
}
if should_backup {
let _ = self.backup_manager.cleanup_old_backups().await;
}
Ok(upgrade_result)
}
Err(e) => {
if let Some(id) = backup_id {
let _ = self.backup_manager.restore_backup(&id).await;
self.last_backup_id = Some(id);
}
Err(e)
}
}
}
pub async fn rollback_last(&self) -> UpgradeResult<Vec<PathBuf>> {
let backup_id = self.last_backup_id.as_ref().ok_or_else(|| UpgradeError::NoBackup {
path: self.workspace_root.join(&self.config.backup.backup_dir),
})?;
self.backup_manager.restore_backup(backup_id).await?;
let metadata = self.backup_manager.list_backups().await?;
let backup_meta = metadata.iter().find(|b| &b.id == backup_id);
Ok(backup_meta.map(|m| m.files.clone()).unwrap_or_default())
}
#[must_use]
pub fn workspace_root(&self) -> &PathBuf {
&self.workspace_root
}
#[must_use]
pub fn config(&self) -> &UpgradeConfig {
&self.config
}
#[must_use]
pub fn registry_client(&self) -> &RegistryClient {
&self.registry_client
}
#[must_use]
pub fn last_backup_id(&self) -> Option<&str> {
self.last_backup_id.as_deref()
}
fn selection_to_detection_options(&self, _selection: &UpgradeSelection) -> DetectionOptions {
DetectionOptions::all()
}
fn collect_package_json_files(
&self,
packages: &[crate::upgrade::detection::PackageUpgrades],
) -> UpgradeResult<Vec<PathBuf>> {
Ok(packages.iter().map(|pkg| pkg.package_path.join("package.json")).collect())
}
}