mod bumper;
mod toml_editor;
mod updater;
pub use bumper::{VersionBump, VersionBumper, BumpPreview};
pub use toml_editor::{TomlEditor, TomlBackup, DependencySection, DependencyInfo};
pub use updater::{
VersionUpdater, UpdateResult, UpdateConfig, ConsistencyReport, UpdatePreview,
VersionInconsistency, InconsistencyType, PackageUpdate, DependencyUpdate, VersionChange,
};
use crate::error::{Result, VersionError};
use crate::workspace::WorkspaceInfo;
use semver::Version;
#[derive(Debug)]
pub struct VersionManager {
workspace: WorkspaceInfo,
updater: VersionUpdater,
}
impl VersionManager {
pub fn new(workspace: WorkspaceInfo) -> Self {
let updater = VersionUpdater::new(workspace.clone());
Self {
workspace,
updater,
}
}
pub fn release_version(&mut self, bump: VersionBump) -> Result<ReleaseVersionResult> {
let current_version_str = self.workspace.workspace_version()?;
let current_version = Version::parse(¤t_version_str)
.map_err(|e| VersionError::ParseFailed {
version: current_version_str,
source: e,
})?;
let bumper = VersionBumper::from_version(current_version.clone());
let new_version = bumper.bump(bump.clone())?;
let consistency_report = self.updater.validate_version_consistency()?;
if !consistency_report.is_consistent() {
return Err(VersionError::DependencyMismatch {
dependency: "workspace".to_string(),
expected: current_version.to_string(),
found: format!("{} inconsistencies", consistency_report.inconsistencies.len()),
}.into());
}
let preview = self.updater.preview_update(&new_version)?;
let update_config = UpdateConfig::default();
let update_result = self.updater.update_workspace_version(&new_version, update_config)?;
self.updater.clear_backups();
Ok(ReleaseVersionResult {
bump_type: bump,
previous_version: current_version,
new_version,
update_result,
preview,
consistency_report,
})
}
pub fn rollback(&self) -> Result<()> {
self.updater.rollback_all_changes()
}
pub fn preview_bump(&self, bump: VersionBump) -> Result<BumpPreviewResult> {
let current_version_str = self.workspace.workspace_version()?;
let current_version = Version::parse(¤t_version_str)
.map_err(|e| VersionError::ParseFailed {
version: current_version_str,
source: e,
})?;
let bumper = VersionBumper::from_version(current_version);
let new_version = bumper.bump(bump.clone())?;
let bump_preview = bumper.preview_bumps()?;
let update_preview = self.updater.preview_update(&new_version)?;
Ok(BumpPreviewResult {
bump_type: bump,
bump_preview,
update_preview,
})
}
pub fn validate_consistency(&self) -> Result<ConsistencyReport> {
self.updater.validate_version_consistency()
}
pub fn current_version(&self) -> Result<Version> {
let version_str = self.workspace.workspace_version()?;
Version::parse(&version_str)
.map_err(|e| VersionError::ParseFailed {
version: version_str,
source: e,
}.into())
}
pub fn uses_workspace_inheritance(&self) -> Result<WorkspaceInheritanceInfo> {
let mut packages_using_inheritance = Vec::new();
let mut packages_with_explicit_versions = Vec::new();
for (package_name, package_info) in &self.workspace.packages {
let editor = TomlEditor::open(&package_info.cargo_toml_path)?;
if editor.uses_workspace_version() {
packages_using_inheritance.push(package_name.clone());
} else {
packages_with_explicit_versions.push(package_name.clone());
}
}
Ok(WorkspaceInheritanceInfo {
packages_using_inheritance,
packages_with_explicit_versions,
total_packages: self.workspace.packages.len(),
})
}
pub fn synchronize_versions(&mut self) -> Result<UpdateResult> {
let current_version = self.current_version()?;
let update_config = UpdateConfig {
create_backups: true,
update_internal_dependencies: true,
preserve_workspace_inheritance: false, };
let result = self.updater.update_workspace_version(¤t_version, update_config)?;
self.updater.clear_backups();
Ok(result)
}
pub fn add_explicit_dependency_versions(&mut self) -> Result<UpdateResult> {
let current_version = self.current_version()?;
let update_config = UpdateConfig {
create_backups: true,
update_internal_dependencies: true,
preserve_workspace_inheritance: true,
};
let result = self.updater.update_workspace_version(¤t_version, update_config)?;
self.updater.clear_backups();
Ok(result)
}
pub fn workspace(&self) -> &WorkspaceInfo {
&self.workspace
}
pub fn updater(&mut self) -> &mut VersionUpdater {
&mut self.updater
}
}
#[derive(Debug, Clone)]
pub struct ReleaseVersionResult {
pub bump_type: VersionBump,
pub previous_version: Version,
pub new_version: Version,
pub update_result: UpdateResult,
pub preview: UpdatePreview,
pub consistency_report: ConsistencyReport,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct BumpPreviewResult {
pub bump_type: VersionBump,
pub bump_preview: BumpPreview,
pub update_preview: UpdatePreview,
}
#[derive(Debug, Clone)]
pub struct WorkspaceInheritanceInfo {
pub packages_using_inheritance: Vec<String>,
pub packages_with_explicit_versions: Vec<String>,
pub total_packages: usize,
}
impl ReleaseVersionResult {
pub fn is_successful(&self) -> bool {
self.update_result.packages_updated > 0 || self.update_result.dependencies_updated > 0
}
pub fn summary(&self) -> String {
format!(
"Version {} → {}: {} packages updated, {} dependencies updated, {} files modified",
self.previous_version,
self.new_version,
self.update_result.packages_updated,
self.update_result.dependencies_updated,
self.update_result.modified_files.len()
)
}
pub fn format_report(&self) -> String {
let mut report = format!("🚀 Version Release: {} → {}\n", self.previous_version, self.new_version);
report.push_str(&format!("Bump Type: {}\n\n", self.bump_type));
report.push_str("📊 Changes Summary:\n");
report.push_str(&format!(" - Packages updated: {}\n", self.update_result.packages_updated));
report.push_str(&format!(" - Dependencies updated: {}\n", self.update_result.dependencies_updated));
report.push_str(&format!(" - Files modified: {}\n\n", self.update_result.modified_files.len()));
if !self.update_result.modified_files.is_empty() {
report.push_str("📝 Modified Files:\n");
for file in &self.update_result.modified_files {
report.push_str(&format!(" - {}\n", file.display()));
}
report.push('\n');
}
report.push_str("✅ Pre-update Validation:\n");
report.push_str(&format!(" {}\n", self.consistency_report.format_report()));
report
}
}
impl BumpPreviewResult {
pub fn format_preview(&self) -> String {
let mut preview = format!("🔍 Version Bump Preview ({})\n\n", self.bump_type);
preview.push_str("📈 Version Options:\n");
preview.push_str(&format!(" {}\n\n", self.bump_preview.format_preview()));
preview.push_str("📋 Workspace Changes:\n");
preview.push_str(&format!(" {}\n", self.update_preview.format_preview()));
preview
}
}
impl WorkspaceInheritanceInfo {
pub fn inheritance_percentage(&self) -> f64 {
if self.total_packages == 0 {
0.0
} else {
(self.packages_using_inheritance.len() as f64 / self.total_packages as f64) * 100.0
}
}
pub fn all_use_inheritance(&self) -> bool {
self.packages_with_explicit_versions.is_empty()
}
pub fn none_use_inheritance(&self) -> bool {
self.packages_using_inheritance.is_empty()
}
pub fn format_info(&self) -> String {
format!(
"Workspace inheritance: {:.1}% ({}/{} packages use inheritance)",
self.inheritance_percentage(),
self.packages_using_inheritance.len(),
self.total_packages
)
}
}
pub fn create_version_manager() -> Result<VersionManager> {
let workspace = WorkspaceInfo::analyze(".")?;
Ok(VersionManager::new(workspace))
}
pub fn quick_version_bump(bump: VersionBump) -> Result<ReleaseVersionResult> {
let mut manager = create_version_manager()?;
manager.release_version(bump)
}
pub fn quick_version_preview(bump: VersionBump) -> Result<BumpPreviewResult> {
let manager = create_version_manager()?;
manager.preview_bump(bump)
}
pub fn quick_consistency_check() -> Result<ConsistencyReport> {
let manager = create_version_manager()?;
manager.validate_consistency()
}