Skip to content

Commit

Permalink
implement basic check flag
Browse files Browse the repository at this point in the history
This implements a very basic version of a check flag. If any
dependencies that should move to the workspace are found, they are
printed to stderr and the command exits with a FAILURE `ExitCode`.

If any dependencies are found in a workspace member, but that have an
existing workspace dependency that can be directly used (that is if the
workspace Cargo.toml doesn't need to be modified) nothing is printed,
but the command still exits with FAILURE.

Ideally the check flag would work like `cargo fmt --check` where a diff
is shown, but that would require extensive refactoring.
  • Loading branch information
azdle authored and hdoordt committed Oct 30, 2024
1 parent da70f52 commit 344a695
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 24 deletions.
68 changes: 45 additions & 23 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -49,7 +56,7 @@ fn rewrite_dep_path_as_relative<P: AsRef<std::path::Path>>(dep: &mut Dependency,
}
}

pub fn auto_inherit(conf: &AutoInheritConf) -> Result<(), anyhow::Error> {
pub fn auto_inherit(conf: &AutoInheritConf) -> Result<ExitCode, anyhow::Error> {
let metadata = guppy::MetadataCommand::new().exec().context(
"Failed to execute `cargo metadata`. Was the command invoked inside a Rust project?",
)?;
Expand Down Expand Up @@ -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(),
Expand All @@ -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())
Expand All @@ -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()
Expand All @@ -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,
);
}
Expand All @@ -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,
);
}
Expand All @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use cargo_autoinherit::{auto_inherit, AutoInheritConf};
use std::process::ExitCode;

use clap::Parser;

Expand All @@ -16,7 +17,7 @@ pub enum CargoInvocation {
AutoInherit(AutoInheritConf),
}

fn main() -> Result<(), anyhow::Error> {
fn main() -> Result<ExitCode, anyhow::Error> {
let cli = CliWrapper::parse();
let CargoInvocation::AutoInherit(conf) = cli.command;
auto_inherit(&conf)
Expand Down

0 comments on commit 344a695

Please sign in to comment.