use crate::error::{CommandExitCode, Result};
use crate::meta::CargoConfig;
use governor_owners::{OwnersClient, resolve_owners, validate_not_empty};
pub struct SyncService {
config: CargoConfig,
client: OwnersClient,
}
impl SyncService {
pub fn new(config: CargoConfig) -> Result<Self> {
let client = OwnersClient::try_new().unwrap_or_else(|_| OwnersClient::new());
Ok(Self { config, client })
}
pub async fn execute(&self, all: bool, dry_run: bool) -> Result<CommandExitCode> {
let packages: Vec<_> = if all {
self.config.packages.iter().collect()
} else {
self.config.current_package().into_iter().collect()
};
let mut partial_success = false;
for pkg in packages {
let result = self.sync_package(pkg, dry_run).await?;
partial_success = partial_success || matches!(result, CommandExitCode::PartialSuccess);
}
if partial_success {
Ok(CommandExitCode::PartialSuccess)
} else {
Ok(CommandExitCode::Success)
}
}
async fn sync_package(
&self,
pkg: &crate::meta::PackageConfig,
dry_run: bool,
) -> Result<CommandExitCode> {
println!("Syncing owners for '{}'...", pkg.name);
let workspace_config = self.config.workspace.as_ref();
let package_config = pkg.owners.as_ref();
if workspace_config.is_none() && package_config.is_none() {
println!(" (no owners configured, skipping)");
println!();
return Ok(CommandExitCode::Success);
}
let resolved = Self::resolve_owners(pkg, workspace_config);
if let Err(e) = validate_not_empty(&resolved.owners, &pkg.name) {
eprintln!(" Error: {e}");
return Ok(CommandExitCode::ConfigError);
}
match self.client.sync(&pkg.name, &resolved.owners, dry_run).await {
Ok(governor_owners::OwnersSyncResult::AlreadyInSync) => {
println!(" Already synchronized.");
println!();
}
Ok(governor_owners::OwnersSyncResult::DryRun(diff)) => {
println!(" Dry run - changes that would be applied:");
println!("{diff}");
println!();
}
Ok(governor_owners::OwnersSyncResult::Success(diff)) => {
Self::display_sync_success(&diff);
}
Ok(governor_owners::OwnersSyncResult::Partial { diff, errors }) => {
return Ok(Self::display_sync_partial(&diff, &errors));
}
Err(e) => {
eprintln!(" Error: {e}");
return Ok(CommandExitCode::RegistryError);
}
}
Ok(CommandExitCode::Success)
}
fn resolve_owners(
pkg: &crate::meta::PackageConfig,
workspace_config: Option<&governor_owners::WorkspaceOwnersConfig>,
) -> governor_owners::ResolvedOwners {
let workspace = workspace_config.cloned().unwrap_or_default();
let package = pkg.owners.clone().unwrap_or_default();
resolve_owners(&workspace, &package)
}
fn display_sync_success(diff: &governor_owners::OwnersDiff) {
println!(" Applying changes:");
println!("{diff}");
for owner in &diff.to_add {
println!(" Added {owner}");
}
for owner in &diff.to_remove {
println!(" Removed {owner}");
}
println!();
}
fn display_sync_partial(
diff: &governor_owners::OwnersDiff,
errors: &[(String, String)],
) -> CommandExitCode {
println!(" Partial success:");
println!("{diff}");
for (owner, error) in errors {
eprintln!(" Failed to {owner}: {error}");
}
println!();
CommandExitCode::PartialSuccess
}
}