diff --git a/src/lib.rs b/src/lib.rs index ab44e12..940bd58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,17 +4,24 @@ use cargo_manifest::{Dependency, DependencyDetail, DepsSet, Manifest}; use guppy::VersionReq; use std::collections::BTreeMap; use std::fmt::Formatter; +use std::process::ExitCode; use toml_edit::{Array, Key}; mod dedup; #[derive(Debug, Default, Clone, clap::Args)] +#[command(about, author, version)] +#[command(group = clap::ArgGroup::new("mode").multiple(false))] pub struct AutoInheritConf { - #[arg( - long, - help = "Represents inherited dependencies as `package.workspace = true` if possible." - )] + /// Represents inherited dependencies as `package.workspace = true` if possible. + #[arg(long)] pub prefer_simple_dotted: bool, + /// Run autoinherit in check mode + /// + /// Instead of automatically fixing non-inherited dependencies, only check that + /// none exist, exiting with a non-zero exit code if any are found. + #[arg(long, group = "mode")] + pub check: bool, } /// Rewrites a `path` dependency as being absolute, based on a given path @@ -49,7 +56,7 @@ fn rewrite_dep_path_as_relative>(dep: &mut Dependency, } } -pub fn auto_inherit(conf: &AutoInheritConf) -> Result<(), anyhow::Error> { +pub fn auto_inherit(conf: &AutoInheritConf) -> Result { let metadata = guppy::MetadataCommand::new().exec().context( "Failed to execute `cargo metadata`. Was the command invoked inside a Rust project?", )?; @@ -143,19 +150,26 @@ pub fn auto_inherit(conf: &AutoInheritConf) -> Result<(), anyhow::Error> { .or_insert(toml_edit::Item::Table(toml_edit::Table::new())) .as_table_mut() .expect("Failed to find `[workspace.dependencies]` table in root manifest."); - let mut was_modified = false; + let mut workspace_was_modified = false; for (package_name, source) in &package_name2inherited_source { if workspace_deps.get(package_name).is_some() { continue; } else { - let mut dep = shared2dep(source); - rewrite_dep_path_as_relative(&mut dep, workspace_root); - - insert_preserving_decor(workspace_deps, package_name, dep2toml_item(&dep)); - was_modified = true; + if conf.check { + eprintln!("Dependency should move to workspace: {}", package_name); + } else { + let mut dep = shared2dep(source); + rewrite_dep_path_as_relative(&mut dep, workspace_root); + insert_preserving_decor( + workspace_deps, + package_name, + dep2toml_item(&shared2dep(source)), + ); + } + workspace_was_modified = true; } } - if was_modified { + if workspace_was_modified && !conf.check { fs_err::write( workspace_root.join("Cargo.toml").as_std_path(), workspace_toml.to_string(), @@ -164,6 +178,7 @@ pub fn auto_inherit(conf: &AutoInheritConf) -> Result<(), anyhow::Error> { } // Inherit new "shared" dependencies in each member's manifest + let mut any_member_was_modified = false; for member_id in graph.workspace().member_ids() { let package = graph.metadata(member_id)?; let manifest_contents = fs_err::read_to_string(package.manifest_path().as_std_path()) @@ -173,7 +188,7 @@ pub fn auto_inherit(conf: &AutoInheritConf) -> Result<(), anyhow::Error> { let mut manifest_toml: toml_edit::DocumentMut = manifest_contents .parse() .context("Failed to parse root manifest")?; - let mut was_modified = false; + let mut member_was_modified = false; if let Some(deps) = &manifest.dependencies { let deps_toml = manifest_toml["dependencies"] .as_table_mut() @@ -182,7 +197,7 @@ pub fn auto_inherit(conf: &AutoInheritConf) -> Result<(), anyhow::Error> { deps, deps_toml, &package_name2inherited_source, - &mut was_modified, + &mut member_was_modified, conf.prefer_simple_dotted, ); } @@ -194,7 +209,7 @@ pub fn auto_inherit(conf: &AutoInheritConf) -> Result<(), anyhow::Error> { deps, deps_toml, &package_name2inherited_source, - &mut was_modified, + &mut member_was_modified, conf.prefer_simple_dotted, ); } @@ -206,20 +221,27 @@ pub fn auto_inherit(conf: &AutoInheritConf) -> Result<(), anyhow::Error> { deps, deps_toml, &package_name2inherited_source, - &mut was_modified, + &mut member_was_modified, conf.prefer_simple_dotted, ); } - if was_modified { - fs_err::write( - package.manifest_path().as_std_path(), - manifest_toml.to_string(), - ) - .context("Failed to write manifest")?; + if member_was_modified { + any_member_was_modified = true; + if !conf.check { + fs_err::write( + package.manifest_path().as_std_path(), + manifest_toml.to_string(), + ) + .context("Failed to write manifest")?; + } } } - Ok(()) + if conf.check && (workspace_was_modified || any_member_was_modified) { + Ok(ExitCode::FAILURE) + } else { + Ok(ExitCode::SUCCESS) + } } enum Action { diff --git a/src/main.rs b/src/main.rs index a58f6ea..c7454e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use cargo_autoinherit::{auto_inherit, AutoInheritConf}; +use std::process::ExitCode; use clap::Parser; @@ -16,7 +17,7 @@ pub enum CargoInvocation { AutoInherit(AutoInheritConf), } -fn main() -> Result<(), anyhow::Error> { +fn main() -> Result { let cli = CliWrapper::parse(); let CargoInvocation::AutoInherit(conf) = cli.command; auto_inherit(&conf)