Skip to content

implement add deps to cargo workspace#9

Merged
Bechma merged 1 commit intocyberfabric:mainfrom
Bechma:fix-add-deps-after-mod-add
Mar 9, 2026
Merged

implement add deps to cargo workspace#9
Bechma merged 1 commit intocyberfabric:mainfrom
Bechma:fix-add-deps-after-mod-add

Conversation

@Bechma
Copy link
Copy Markdown
Collaborator

@Bechma Bechma commented Mar 6, 2026

Summary by CodeRabbit

  • New Features

    • Workspace-aware module generation with centralized dependency resolution and safer workspace manifest updates.
  • Documentation

    • Added AGENTS.md with recommended cargo workflows (clippy, fmt, test, prefer cargo add).
  • Chores

    • Added semver crate to the workspace dependencies.

@Bechma Bechma force-pushed the fix-add-deps-after-mod-add branch from b0318d6 to f76f726 Compare March 9, 2026 10:52
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 9, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5b52c900-f80b-4fba-a69c-b9cce3fc1be3

📥 Commits

Reviewing files that changed from the base of the PR and between 018f48e and 8002fab.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • AGENTS.md
  • Cargo.toml
  • crates/cli/Cargo.toml
  • crates/cli/src/mod/add.rs
✅ Files skipped from review due to trivial changes (1)
  • AGENTS.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • Cargo.toml
  • crates/cli/Cargo.toml

📝 Walkthrough

Walkthrough

Refactors the CLI module-add flow to be workspace-aware: module generation now returns generated paths; modules are validated and staged, manifests rewritten to workspace inheritance; dependencies are collected, semver-merged and applied to the root Cargo.toml. Adds semver workspace dependency and many TOML/semver helpers.

Changes

Cohort / File(s) Summary
Workspace Root Manifests
Cargo.toml
Added semver = "1.0" to workspace dependencies.
CLI Crate Manifest
crates/cli/Cargo.toml
Added semver = { workspace = true } to the CLI crate dependencies.
CLI Module Addition Logic
crates/cli/src/mod/add.rs
Large refactor: generate_module now returns Vec<String>; introduced workspace-aware pipeline (ensure_modules_directory, prepare_generated_modules, update_workspace_cargo_toml) and StagedModuleWrite. Added many helpers for dependency normalization, workspace-relative path resolution, semver comparison/upgrade, TOML mutation and save utilities, and rewriting manifests to inherit workspace dependency settings. Significant signature changes for dependency parsing and module preparation; many new functions and logic for merging/updating root Cargo.toml.
Documentation
AGENTS.md
New file with cargo workflow guidelines (clippy, fmt/test, prefer cargo add).

Sequence Diagram

sequenceDiagram
    rect rgba(200,230,255,0.5)
    actor CLI
    end
    participant Generator as ModuleGenerator
    participant Preparer as ModulePreparer
    participant Resolver as DepResolver
    participant Updater as WorkspaceUpdater
    participant FS as FileSystem

    CLI->>Generator: generate_module()
    Generator->>FS: create module files
    Generator-->>CLI: return generated_paths

    CLI->>Preparer: ensure_modules_directory(workspace_root)
    Preparer->>FS: check/create modules/ directory
    Preparer-->>CLI: ok

    CLI->>Preparer: prepare_generated_modules(workspace_root, generated_paths)
    Preparer->>FS: read each module Cargo.toml
    Preparer->>Resolver: get_dependencies(doc, source_dir, workspace_root)
    Resolver->>Resolver: normalize names, resolve workspace-relative paths, parse versions
    Resolver-->>Preparer: collected dependencies
    Preparer->>FS: rewrite module manifests to workspace inheritance
    Preparer-->>CLI: staged_module_writes + deps

    CLI->>Updater: update_workspace_cargo_toml(workspace_root, generated_paths, deps)
    Updater->>FS: read root Cargo.toml
    Updater->>Updater: add members, merge/upsert deps (semver logic)
    Updater->>FS: write updated root Cargo.toml
    Updater-->>CLI: success
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐇 I hopped through Cargo files late at night,

I nudged dependencies toward workspace light,
Semver whispered, versions aligned,
Modules marched into members signed,
A tidy root — the rabbit's delight.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: implementing functionality to add dependencies to a Cargo workspace, which aligns with the substantial refactoring in add.rs that introduces workspace-aware dependency handling.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/cli/src/mod/add.rs (1)

332-336: Consider warning when falling back to wildcard version.

Using "*" as the fallback version (line 335) enables the workflow to proceed when a module template doesn't specify a version, but wildcard versions can lead to non-reproducible builds. Consider logging a warning when this fallback is used to alert users that they should pin the version.

💡 Optional: Add a warning for wildcard fallback
     if let Some(version) = metadata.version {
         dep_table.insert("version", version.into());
     } else {
+        eprintln!("warning: no version specified for dependency, using wildcard '*'");
         dep_table.insert("version", "*".into());
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/cli/src/mod/add.rs` around lines 332 - 336, When falling back to the
wildcard version ("*") in the metadata.version None branch, emit a warning to
alert users that using "*" can lead to non-reproducible builds and they should
pin versions; locate the block that checks metadata.version and inserts into
dep_table (the dep_table.insert("version", "*".into()) branch) and add a warning
via the project's logging facility (e.g., warn! / tracing::warn! / log::warn!)
immediately after inserting the wildcard, including the module name or
identifier if available to give context.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/cli/src/mod/add.rs`:
- Around line 332-336: When falling back to the wildcard version ("*") in the
metadata.version None branch, emit a warning to alert users that using "*" can
lead to non-reproducible builds and they should pin versions; locate the block
that checks metadata.version and inserts into dep_table (the
dep_table.insert("version", "*".into()) branch) and add a warning via the
project's logging facility (e.g., warn! / tracing::warn! / log::warn!)
immediately after inserting the wildcard, including the module name or
identifier if available to give context.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6236be12-c07a-4e6a-aeff-3c53f8775cf2

📥 Commits

Reviewing files that changed from the base of the PR and between b0da5bf and f76f726.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • Cargo.toml
  • crates/cli/Cargo.toml
  • crates/cli/src/mod/add.rs

@Bechma Bechma force-pushed the fix-add-deps-after-mod-add branch from f76f726 to 9ae0bed Compare March 9, 2026 11:14
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
crates/cli/src/mod/add.rs (5)

336-343: Consider logging the dependency name in the wildcard warning.

When falling back to *, the warning doesn't identify which dependency triggered it, making debugging harder.

Note: This function doesn't have access to the dependency name. You'd need to pass it as a parameter or restructure the warning.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/cli/src/mod/add.rs` around lines 336 - 343, The warning lacks the
dependency name; modify the function that builds dep_table (the one using
variables metadata, dep_table and inserting path/version) to accept the
dependency name (e.g., dependency_name: &str) or otherwise obtain it from the
caller, then change the fallback branch to eprintln! including that
dependency_name (e.g., "warning: no version specified for dependency '{}', using
wildcard '*'" ), and update all call sites to pass the dependency name so the
log pinpoints which dependency fell back to '*'.

604-611: Minor: Prefer .first() over .get(0) for idiomatic Rust.

💡 Suggested change
-        assert_eq!(
-            features.get(0).and_then(toml_edit::Value::as_str),
-            Some("json")
-        );
+        assert_eq!(
+            features.first().and_then(toml_edit::Value::as_str),
+            Some("json")
+        );

Same applies to lines 718.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/cli/src/mod/add.rs` around lines 604 - 611, The code uses
features.get(0).and_then(toml_edit::Value::as_str) which is unidiomatic; change
to features.first().and_then(toml_edit::Value::as_str) in the assertion that
checks the first feature (the features variable and its assertion block), and
make the same replacement at the other occurrence around the second assertion
near line 718 so both checks use .first() instead of .get(0).

206-209: Silent skip of unknown dependency formats could hide issues.

If a dependency has an unexpected TOML structure (neither string, table, nor inline table), it's silently skipped. While this is defensive, a warning would help debug template issues.

💡 Optional: add trace/debug logging
             } else {
+                eprintln!("warning: skipping dependency '{name}' with unsupported format");
                 continue;
             };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/cli/src/mod/add.rs` around lines 206 - 209, The code currently
silently continues when a dependency value is neither a string, table, nor
inline table; change that silent skip to emit a warning so template issues are
visible. In the branch that handles dependency value types (the else { continue;
} branch that follows the string/table/inline_table checks), replace the silent
continue with a warning log (e.g., tracing::warn! or log::warn!) that includes
the dependency key and the unexpected value (or its type) and then continue;
keep the rest of the control flow the same so non-conforming TOML still skips
but is logged for debugging.

743-763: Tests use absolute Unix-style paths that may fail on Windows CI.

The test at line 754-755 uses /repo/modules/rest-gateway and /repo which won't work correctly on Windows where paths start with drive letters.

💡 Consider using platform-agnostic test paths

If Windows CI support is needed, consider using tempdir or constructing paths relative to std::env::current_dir():

use std::env;
let repo = env::current_dir().unwrap();
let module_path = repo.join("modules").join("rest-gateway");

Or skip these tests on Windows with #[cfg(unix)] if Windows support isn't a priority.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/cli/src/mod/add.rs` around lines 743 - 763, The test
collects_dependencies_with_workspace_relative_paths uses hard-coded absolute
Unix-style paths ("/repo/...") which will fail on Windows; update the test to
build platform-agnostic paths (e.g., use std::env::current_dir() or a tempdir
and PathBuf::join) and pass those PathBufs into get_dependencies instead of
literal strings so Path operations work on Windows and Unix; locate the test
function collects_dependencies_with_workspace_relative_paths and replace the
Path::new("/repo/modules/rest-gateway") and Path::new("/repo") inputs with
constructed PathBuf values derived from current_dir() or a temp directory before
calling get_dependencies, keeping the same assertions on sdk_dep.path.

377-380: Fallback to wildcard version in edge case may mask issues.

Line 378 falls back to "*" if existing_dep.as_str() returns None. By the time this branch is reached, existing_dep should always be a string (since table/inline branches returned early). If it's None, something unexpected happened.

Consider using unwrap_or_else with a warning, or an explicit assertion.

💡 Optional: Add defensive logging
     let mut dep_table = toml_edit::InlineTable::new();
-    dep_table.insert("version", existing_dep.as_str().unwrap_or("*").into());
+    let version = existing_dep.as_str().unwrap_or_else(|| {
+        eprintln!("warning: unexpected dependency format, using wildcard version");
+        "*"
+    });
+    dep_table.insert("version", version.into());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/cli/src/mod/add.rs` around lines 377 - 380, The code currently falls
back to "*" via existing_dep.as_str().unwrap_or("*"), which masks an unexpected
non-string existing_dep; update the handling so you explicitly check
existing_dep.as_str() (referencing existing_dep and dep_table and the assignment
to toml_edit::Item::Value) and if it is None either emit a defensive warning
with context (e.g., using the repository logger/tracing) and return an Err, or
assert/fail fast (debug_assert or panic) instead of silently using "*";
implement this explicit branch (log/error + early return or assert) rather than
unwrap_or to make the unexpected state visible.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/cli/src/mod/add.rs`:
- Around line 128-133: Currently CargoTomlDependencies is merged via
HashMap::extend in the loop (see generated_modules loop, get_dependencies, and
CargoTomlDependencies usage), which silently overwrites duplicate dependency
entries; change the merge to check for existing keys and when a conflict is
found call should_replace_with_newer_semver(old_version, new_version) to decide
whether to keep/replace, and log a warning when versions are incompatible or
when an overwrite occurs; specifically replace the direct
dependencies.extend(...) with an iterative merge that inspects existing entries
from CargoTomlDependencies before inserting, uses
should_replace_with_newer_semver to pick the newer semver, and emits a clear
warning mentioning the dependency name and both versions when a change happens.

---

Nitpick comments:
In `@crates/cli/src/mod/add.rs`:
- Around line 336-343: The warning lacks the dependency name; modify the
function that builds dep_table (the one using variables metadata, dep_table and
inserting path/version) to accept the dependency name (e.g., dependency_name:
&str) or otherwise obtain it from the caller, then change the fallback branch to
eprintln! including that dependency_name (e.g., "warning: no version specified
for dependency '{}', using wildcard '*'" ), and update all call sites to pass
the dependency name so the log pinpoints which dependency fell back to '*'.
- Around line 604-611: The code uses
features.get(0).and_then(toml_edit::Value::as_str) which is unidiomatic; change
to features.first().and_then(toml_edit::Value::as_str) in the assertion that
checks the first feature (the features variable and its assertion block), and
make the same replacement at the other occurrence around the second assertion
near line 718 so both checks use .first() instead of .get(0).
- Around line 206-209: The code currently silently continues when a dependency
value is neither a string, table, nor inline table; change that silent skip to
emit a warning so template issues are visible. In the branch that handles
dependency value types (the else { continue; } branch that follows the
string/table/inline_table checks), replace the silent continue with a warning
log (e.g., tracing::warn! or log::warn!) that includes the dependency key and
the unexpected value (or its type) and then continue; keep the rest of the
control flow the same so non-conforming TOML still skips but is logged for
debugging.
- Around line 743-763: The test
collects_dependencies_with_workspace_relative_paths uses hard-coded absolute
Unix-style paths ("/repo/...") which will fail on Windows; update the test to
build platform-agnostic paths (e.g., use std::env::current_dir() or a tempdir
and PathBuf::join) and pass those PathBufs into get_dependencies instead of
literal strings so Path operations work on Windows and Unix; locate the test
function collects_dependencies_with_workspace_relative_paths and replace the
Path::new("/repo/modules/rest-gateway") and Path::new("/repo") inputs with
constructed PathBuf values derived from current_dir() or a temp directory before
calling get_dependencies, keeping the same assertions on sdk_dep.path.
- Around line 377-380: The code currently falls back to "*" via
existing_dep.as_str().unwrap_or("*"), which masks an unexpected non-string
existing_dep; update the handling so you explicitly check existing_dep.as_str()
(referencing existing_dep and dep_table and the assignment to
toml_edit::Item::Value) and if it is None either emit a defensive warning with
context (e.g., using the repository logger/tracing) and return an Err, or
assert/fail fast (debug_assert or panic) instead of silently using "*";
implement this explicit branch (log/error + early return or assert) rather than
unwrap_or to make the unexpected state visible.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4051903b-1618-4b07-88f8-bf836f6ba1bc

📥 Commits

Reviewing files that changed from the base of the PR and between f76f726 and 9ae0bed.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • Cargo.toml
  • crates/cli/Cargo.toml
  • crates/cli/src/mod/add.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/cli/Cargo.toml
  • Cargo.toml

Comment thread crates/cli/src/mod/add.rs Outdated
@Bechma Bechma force-pushed the fix-add-deps-after-mod-add branch from 9ae0bed to 018f48e Compare March 9, 2026 11:34
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/cli/src/mod/add.rs`:
- Around line 64-65: prepare_generated_modules currently persists each generated
module Cargo.toml (setting workspace = true) before update_workspace_cargo_toml
runs, which can leave modules pointing to non-existent workspace members if the
root update fails; change the flow so prepare_generated_modules returns the
in-memory representations (e.g., a Vec of (module_path, rewritten_toml_string)
or a GeneratedModule struct) without writing files, then call
update_workspace_cargo_toml(&self.path, &generated_modules, dependencies) and
only if that call succeeds iterate the staged data and write the rewritten
manifests to disk; update references to generated_modules and any callers to use
the staged data return type and ensure error handling aborts without writing
module files when the root update fails.
- Around line 133-153: The conflict handler in get_dependencies currently only
compares versions (using should_replace_with_newer_semver) and inserts the
chosen incoming dependency, dropping other metadata (default_features, path,
package, features); update the logic that processes (name, incoming) vs existing
so that whenever you insert/replace you also merge metadata: reconcile
default_features by preferring false over true (i.e., if either is false, set
false), union features lists, and preserve non-empty path and package values
(prefer the explicit/local value over empty/workspace when merging); apply this
merge both when should_replace_with_newer_semver decides to replace and when
versions are equal (old_ver == new_ver) so you never silently drop
default_features/path/package from existing or incoming entries.
- Around line 333-336: In add_dependencies_to_workspace, existing workspace
entries (workspace_deps.get_mut(&name)) currently only call
maybe_upgrade_workspace_dep_version and
maybe_apply_workspace_dep_default_features and then continue, which silently
drops incoming source-defining keys; change this logic to detect incoming
metadata.package and metadata.path: if the existing_dep lacks a package or path,
merge the incoming package/path into existing_dep; if existing_dep has a
conflicting package or path value (different from incoming), return/fail with a
clear error mentioning the dependency name and the conflicting key; keep the
current behavior for version and default-features via
maybe_upgrade_workspace_dep_version and
maybe_apply_workspace_dep_default_features but ensure source-defining keys are
never silently discarded.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 89412f0e-c1d3-4f32-ac6e-fe259e662b45

📥 Commits

Reviewing files that changed from the base of the PR and between 9ae0bed and 018f48e.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • Cargo.toml
  • crates/cli/Cargo.toml
  • crates/cli/src/mod/add.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/cli/Cargo.toml

Comment thread crates/cli/src/mod/add.rs Outdated
Comment thread crates/cli/src/mod/add.rs
Comment thread crates/cli/src/mod/add.rs
Signed-off-by: Bechma <19294519+Bechma@users.noreply.github.com>
@Bechma Bechma force-pushed the fix-add-deps-after-mod-add branch from 018f48e to 8002fab Compare March 9, 2026 13:53
@Bechma Bechma merged commit 30ef4f7 into cyberfabric:main Mar 9, 2026
1 check passed
@Bechma Bechma deleted the fix-add-deps-after-mod-add branch March 13, 2026 08:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant