Skip to content

Commit f48c97e

Browse files
committed
cargo-rail: fixed the WorkspaceContext not being passed efficiently; all commands now use the single main.rs WorkspaceContext; added rustdocs
1 parent 52cd5e5 commit f48c97e

File tree

12 files changed

+153
-131
lines changed

12 files changed

+153
-131
lines changed

src/cargo/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
//! Cargo workspace integration and manipulation
2+
//!
3+
//! This module provides utilities for working with Cargo workspaces:
4+
//!
5+
//! - **metadata**: Load and query Cargo.toml metadata using cargo_metadata
6+
//! - **transform**: Transform Cargo.toml files (flatten workspace inheritance, convert path deps)
7+
//! - **files**: Discover and copy auxiliary files (.cargo, rust-toolchain, etc.)
8+
//! - **helpers**: Cargo-specific utility functions
9+
110
pub mod files;
211
pub mod helpers;
312
pub mod metadata;

src/commands/doctor.rs

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,22 @@
22
//!
33
//! The doctor command runs all health checks and reports any issues found.
44
5-
use std::env;
6-
75
use crate::checks::{CheckContext, create_default_runner};
6+
use crate::core::context::WorkspaceContext;
87
use crate::core::error::{ExitCode, RailError, RailResult};
98

109
/// Run the doctor command to diagnose issues
1110
///
1211
/// Returns Ok(()) if all checks pass, or exits with error code if checks fail
13-
pub fn run_doctor(thorough: bool, json: bool) -> RailResult<()> {
14-
let current_dir = env::current_dir()?;
15-
16-
let ctx = CheckContext {
17-
workspace_root: current_dir,
12+
pub fn run_doctor(ctx: &WorkspaceContext, thorough: bool, json: bool) -> RailResult<()> {
13+
let check_ctx = CheckContext {
14+
workspace_root: ctx.workspace_root().to_path_buf(),
1815
crate_name: None,
1916
thorough,
2017
};
2118

2219
let runner = create_default_runner();
23-
let results = runner.run_all(&ctx)?;
20+
let results = runner.run_all(&check_ctx)?;
2421

2522
if json {
2623
// JSON output for CI/automation
@@ -83,31 +80,27 @@ pub fn run_doctor(thorough: bool, json: bool) -> RailResult<()> {
8380
///
8481
/// This is useful for commands that want to verify the environment is ready
8582
/// before starting work. Returns true if all checks pass, false otherwise.
86-
pub fn run_preflight_check(thorough: bool) -> RailResult<bool> {
87-
let current_dir = env::current_dir()?;
88-
89-
let ctx = CheckContext {
90-
workspace_root: current_dir,
83+
pub fn run_preflight_check(ctx: &WorkspaceContext, thorough: bool) -> RailResult<bool> {
84+
let check_ctx = CheckContext {
85+
workspace_root: ctx.workspace_root().to_path_buf(),
9186
crate_name: None,
9287
thorough,
9388
};
9489

9590
let runner = create_default_runner();
96-
runner.run_all_and_check(&ctx)
91+
runner.run_all_and_check(&check_ctx)
9792
}
9893

9994
/// Run checks for a specific crate
10095
///
10196
/// Useful for validating a single crate before split/sync operations
102-
pub fn run_crate_check(crate_name: &str, thorough: bool) -> RailResult<bool> {
103-
let current_dir = env::current_dir()?;
104-
105-
let ctx = CheckContext {
106-
workspace_root: current_dir,
97+
pub fn run_crate_check(ctx: &WorkspaceContext, crate_name: &str, thorough: bool) -> RailResult<bool> {
98+
let check_ctx = CheckContext {
99+
workspace_root: ctx.workspace_root().to_path_buf(),
107100
crate_name: Some(crate_name.to_string()),
108101
thorough,
109102
};
110103

111104
let runner = create_default_runner();
112-
runner.run_all_and_check(&ctx)
105+
runner.run_all_and_check(&check_ctx)
113106
}

src/commands/lint.rs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
//! Lint command implementation
22
33
use crate::checks::{CheckContext, Severity, create_manifest_runner};
4-
use crate::core::config::RailConfig;
4+
use crate::core::context::WorkspaceContext;
55
use crate::core::error::{RailError, RailResult};
66
use crate::lint::{DepsLinter, DepsReport, VersionsLinter, VersionsReport};
77
use cargo_metadata::MetadataCommand;
8-
use std::env;
98

109
/// Run the lint deps command
11-
pub fn run_lint_deps(fix: bool, apply: bool, json: bool, strict: bool) -> RailResult<()> {
10+
pub fn run_lint_deps(ctx: &WorkspaceContext, fix: bool, apply: bool, json: bool, strict: bool) -> RailResult<()> {
1211
// Load workspace metadata
1312
let metadata = MetadataCommand::new()
14-
.current_dir(env::current_dir()?)
13+
.current_dir(ctx.workspace_root())
1514
.exec()
1615
.map_err(|e| RailError::message(format!("Failed to load workspace metadata: {}", e)))?;
1716

@@ -130,16 +129,16 @@ fn print_fix_report(report: &crate::lint::DepsFixReport) {
130129
}
131130

132131
/// Run the lint versions command
133-
pub fn run_lint_versions(fix: bool, apply: bool, json: bool, strict: bool) -> RailResult<()> {
132+
pub fn run_lint_versions(ctx: &WorkspaceContext, fix: bool, apply: bool, json: bool, strict: bool) -> RailResult<()> {
134133
// Load workspace metadata
135134
let metadata = MetadataCommand::new()
136-
.current_dir(env::current_dir()?)
135+
.current_dir(ctx.workspace_root())
137136
.exec()
138137
.map_err(|e| RailError::message(format!("Failed to load workspace metadata: {}", e)))?;
139138

140139
// Try to load policy config
141-
let config = RailConfig::load(&env::current_dir()?).ok();
142-
let policy = config.as_ref().map(|c| c.policy.clone());
140+
let config = ctx.config.as_ref();
141+
let policy = config.map(|c| c.policy.clone());
143142

144143
let linter = VersionsLinter::new(metadata, policy);
145144

@@ -282,19 +281,17 @@ fn print_versions_fix_report(report: &crate::lint::VersionsFixReport) {
282281
}
283282

284283
/// Run the lint manifest command
285-
pub fn run_lint_manifest(json: bool, strict: bool) -> RailResult<()> {
286-
let workspace_root = env::current_dir()?;
287-
284+
pub fn run_lint_manifest(ctx: &WorkspaceContext, json: bool, strict: bool) -> RailResult<()> {
288285
// Create context for checks
289-
let ctx = CheckContext {
290-
workspace_root: workspace_root.clone(),
286+
let check_ctx = CheckContext {
287+
workspace_root: ctx.workspace_root().to_path_buf(),
291288
crate_name: None,
292289
thorough: false, // Manifest checks are never expensive
293290
};
294291

295292
// Run manifest-specific checks
296293
let runner = create_manifest_runner();
297-
let results = runner.run_all(&ctx)?;
294+
let results = runner.run_all(&check_ctx)?;
298295

299296
// Output results
300297
if json {

src/commands/mappings.rs

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use serde::{Deserialize, Serialize};
2-
use std::env;
32
use std::process::Command;
43

5-
use crate::core::config::RailConfig;
4+
use crate::core::context::WorkspaceContext;
65
use crate::core::error::{ConfigError, RailError, RailResult};
76
use crate::core::mapping::MappingStore;
87
use crate::ui::progress::FileProgress;
@@ -56,17 +55,8 @@ pub struct IntegrityCheck {
5655
}
5756

5857
/// Run the mappings command
59-
pub fn run_mappings(crate_name: String, check: bool, json: bool) -> RailResult<()> {
60-
let current_dir = env::current_dir()?;
61-
62-
// Load configuration
63-
if !RailConfig::exists(&current_dir) {
64-
return Err(RailError::Config(ConfigError::NotFound {
65-
workspace_root: current_dir,
66-
}));
67-
}
68-
69-
let config = RailConfig::load(&current_dir)?;
58+
pub fn run_mappings(ctx: &WorkspaceContext, crate_name: String, check: bool, json: bool) -> RailResult<()> {
59+
let config = ctx.require_config()?.as_ref();
7060

7161
// Find crate configuration
7262
let split_config = config.splits.iter().find(|s| s.name == crate_name).ok_or_else(|| {
@@ -78,7 +68,7 @@ pub fn run_mappings(crate_name: String, check: bool, json: bool) -> RailResult<(
7868
// Load mappings
7969
let notes_ref = format!("refs/notes/rail/{}", crate_name);
8070
let mut mapping_store = MappingStore::new(crate_name.clone());
81-
mapping_store.load(&current_dir)?;
71+
mapping_store.load(ctx.workspace_root())?;
8272

8373
let raw_mappings = mapping_store.all_mappings();
8474

@@ -104,8 +94,8 @@ pub fn run_mappings(crate_name: String, check: bool, json: bool) -> RailResult<(
10494

10595
if check {
10696
// Verify both commits exist
107-
let mono_exists = commit_exists(&current_dir, mono_sha)?;
108-
let remote_exists = if let Some(target_path) = get_target_path(&current_dir, split_config) {
97+
let mono_exists = commit_exists(ctx.workspace_root(), mono_sha)?;
98+
let remote_exists = if let Some(target_path) = get_target_path(ctx.workspace_root(), split_config) {
10999
commit_exists(&target_path, remote_sha)?
110100
} else {
111101
false

src/commands/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
1+
//! CLI commands for cargo-rail
2+
//!
3+
//! This module contains all user-facing command implementations:
4+
//!
5+
//! ## Setup & Inspection
6+
//! - **init**: Initialize rail.toml configuration for a workspace
7+
//! - **doctor**: Run health checks and validation
8+
//! - **status**: Show split/sync status for all crates
9+
//! - **mappings**: View git commit mappings for split crates
10+
//!
11+
//! ## Split & Sync (Pillar 2)
12+
//! - **split**: Split monorepo crates to separate repositories
13+
//! - **sync**: Bidirectional sync between monorepo and split repos
14+
//!
15+
//! ## Graph Operations (Pillar 1)
16+
//! - **affected**: Find crates affected by changes
17+
//! - **test**: Run tests for affected crates only
18+
//! - **check**: Run cargo check for affected crates
19+
//! - **clippy**: Run clippy for affected crates
20+
//!
21+
//! ## Linting (Pillar 3)
22+
//! - **lint**: Workspace policy enforcement (deps, versions, manifest)
23+
//!
24+
//! ## Releases (Pillar 4)
25+
//! - **release**: Release planning and execution with changelog generation
26+
//!
27+
//! All commands accept `&WorkspaceContext` to avoid redundant workspace loads.
28+
129
pub mod affected;
230
pub mod check;
331
pub mod clippy;

src/commands/release.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,15 @@
66
//! 3. Every release has name + version + last_sha
77
88
use crate::core::config::ReleaseConfig;
9+
use crate::core::context::WorkspaceContext;
910
use crate::core::error::{RailError, RailResult};
1011
use crate::quality::changelog::{Changelog, ChangelogFormat, ConventionalCommit};
1112
use crate::release::{ReleasePlan, ReleaseTracker, VersionBump};
12-
use std::env;
1313

1414
/// Run the release plan command
15-
pub fn run_release_plan(name: Option<String>, all: bool, json: bool) -> RailResult<()> {
16-
let workspace_root = env::current_dir()?;
17-
15+
pub fn run_release_plan(ctx: &WorkspaceContext, name: Option<String>, all: bool, json: bool) -> RailResult<()> {
1816
// Load release tracker
19-
let tracker = ReleaseTracker::load(&workspace_root)?;
17+
let tracker = ReleaseTracker::load(ctx.workspace_root())?;
2018

2119
// Determine which releases to plan
2220
let releases: Vec<_> = if all {
@@ -47,7 +45,7 @@ pub fn run_release_plan(name: Option<String>, all: bool, json: bool) -> RailResu
4745
// Analyze each release
4846
let mut plans = Vec::new();
4947
for release in &releases {
50-
let plan = ReleasePlan::analyze(&workspace_root, release)?;
48+
let plan = ReleasePlan::analyze(ctx.workspace_root(), release)?;
5149
plans.push(plan);
5250
}
5351

@@ -63,15 +61,14 @@ pub fn run_release_plan(name: Option<String>, all: bool, json: bool) -> RailResu
6361

6462
/// Run the release apply command
6563
pub fn run_release_apply(
64+
ctx: &WorkspaceContext,
6665
name: String,
6766
dry_run: bool,
6867
// Skip syncing to split repos - deferred to post-v1 (see TODO.md Post-v1: Auto-sync to split repos)
6968
#[allow(unused_variables)] skip_sync: bool,
7069
) -> RailResult<()> {
71-
let workspace_root = env::current_dir()?;
72-
7370
// Load release tracker
74-
let mut tracker = ReleaseTracker::load(&workspace_root)?;
71+
let mut tracker = ReleaseTracker::load(ctx.workspace_root())?;
7572

7673
// Find the release
7774
let release = tracker
@@ -80,7 +77,7 @@ pub fn run_release_apply(
8077
.clone();
8178

8279
// Generate plan
83-
let plan = ReleasePlan::analyze(&workspace_root, &release)?;
80+
let plan = ReleasePlan::analyze(ctx.workspace_root(), &release)?;
8481

8582
if !plan.has_changes {
8683
println!("⚠️ No changes detected for '{}'", name);
@@ -125,25 +122,29 @@ pub fn run_release_apply(
125122
println!("✅ Applying release...");
126123

127124
// 1. Update Cargo.toml version
128-
update_crate_version(&workspace_root, &release.crate_path, &plan.proposed_version.to_string())?;
125+
update_crate_version(
126+
ctx.workspace_root(),
127+
&release.crate_path,
128+
&plan.proposed_version.to_string(),
129+
)?;
129130
println!(" Updated Cargo.toml version");
130131

131132
// 2. Generate and update changelog (if configured)
132133
if let Some(changelog_path) = &release.changelog {
133-
generate_changelog(&workspace_root, &release, &plan, changelog_path)?;
134+
generate_changelog(ctx.workspace_root(), &release, &plan, changelog_path)?;
134135
println!(" Updated {}", changelog_path.display());
135136
}
136137

137138
// 3. Get current HEAD SHA
138-
let head_sha = get_head_sha(&workspace_root)?;
139+
let head_sha = get_head_sha(ctx.workspace_root())?;
139140

140141
// 4. Update rail.toml metadata
141142
tracker.update_release(&name, &plan.proposed_version.to_string(), &head_sha)?;
142143
tracker.save()?;
143144
println!(" Updated rail.toml metadata");
144145

145146
// 5. Create git tag
146-
create_git_tag(&workspace_root, &name, &plan.proposed_version.to_string())?;
147+
create_git_tag(ctx.workspace_root(), &name, &plan.proposed_version.to_string())?;
147148
println!(" Created tag: {}-v{}", name, plan.proposed_version);
148149

149150
println!();

0 commit comments

Comments
 (0)