use std::fs;
use anyhow::{Context, Result, bail};
use crate::cli::util::pluralized;
use crate::{
SyncOptions, SyncReport, WorkspaceManifest, preview_sync_manifest, render_sync_diff,
sync_manifest,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SyncCommandOptions {
pub sync: SyncOptions,
pub diff: bool,
}
pub fn run(workspace: &WorkspaceManifest, options: SyncCommandOptions) -> Result<()> {
let mut changed_packages = 0usize;
let mut check_failed = false;
for package in &workspace.packages {
let preview = if options.diff {
Some(preview_sync_manifest(
&package.manifest_path,
&options.sync,
)?)
} else {
None
};
let report = match &preview {
Some(preview) => preview.report.clone(),
None => sync_manifest(&package.manifest_path, &options.sync)?,
};
let package_name = report.package_name.as_deref().unwrap_or("unknown-package");
if report.changed() {
changed_packages += 1;
if options.sync.check_only || options.diff {
check_failed = true;
println!(
"sync drift in `{package_name}`: {}",
change_summary(&report)
);
} else {
println!("synced `{package_name}`: {}", change_summary(&report));
}
for feature in &report.added_features {
println!(" + {feature}");
}
for feature in &report.removed_features {
println!(" - {feature}");
}
if let Some(preview) = &preview {
if let Some(rewritten) = &preview.rewritten {
let before = fs::read_to_string(&package.manifest_path).with_context(|| {
format!(
"failed to read manifest `{}` for diff output",
package.manifest_path.display()
)
})?;
print!(
"{}",
render_sync_diff(&package.manifest_path, &before, rewritten)
);
}
}
} else {
println!("`{package_name}` is already in sync");
}
}
if changed_packages > 0 && !options.sync.check_only && !options.diff {
println!("updated {changed_packages} package(s)");
}
if check_failed {
bail!("sync drift detected");
}
Ok(())
}
pub fn change_summary(report: &SyncReport) -> String {
let mut parts = Vec::new();
if !report.added_features.is_empty() {
parts.push(format!(
"added {}",
pluralized(report.added_features.len(), "feature")
));
}
if !report.removed_features.is_empty() {
parts.push(format!(
"removed {} stale metadata entr{}",
report.removed_features.len(),
if report.removed_features.len() == 1 {
"y"
} else {
"ies"
}
));
}
if parts.is_empty() {
parts.push("layout updated".to_owned());
}
parts.push(format!("using `{}` layout", report.style));
parts.join(", ")
}