28 releases
Uses new Rust 2024
| 0.3.16 | Dec 11, 2025 |
|---|---|
| 0.3.15 | Dec 11, 2025 |
| 0.3.9 | Nov 30, 2025 |
| 0.2.12 | Nov 27, 2025 |
#266 in Cargo plugins
Used in lmrc-cli
125KB
2K
SLoC
lmrc-cargo-workspace
Part of the LMRC Stack - Infrastructure-as-Code toolkit for building production-ready Rust applications
A comprehensive Rust library for managing Cargo workspaces programmatically.
Features
- Workspace Discovery: Automatically find and parse Cargo workspaces
- Member Detection: Identify all workspace members (binaries and libraries)
- Dependency Analysis: Analyze dependency relationships and build orders
- Version Management: Update versions across workspace members using semver
- Programmatic Build/Test: Build and test with different configurations
- Selective Operations: Build/test only specific members based on patterns, changes, or dependencies
- Parallel Execution: Build and test workspace members in parallel
- Cargo Metadata Integration: Access resolved dependency information and features
- Workspace Validation: Lint for version inconsistencies, formatting issues, and code quality
- Format Preservation: Parse and update Cargo.toml files while preserving formatting and comments
- Comprehensive Error Handling: Detailed error types for all operations
Installation
Add this to your Cargo.toml:
[dependencies]
lmrc-cargo-workspace = "0.1"
Quick Start
use lmrc_cargo_workspace::{
workspace::Workspace,
build::{WorkspaceBuilder, BuildConfig},
version::{VersionManager, BumpType, VersionUpdateStrategy},
dependency::DependencyGraph,
};
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Discover workspace
let workspace = Workspace::discover(Path::new("."))?;
println!("Found workspace at: {}", workspace.root.display());
// Analyze dependencies
let graph = DependencyGraph::from_workspace(&workspace)?;
let build_order = graph.topological_sort()?;
println!("Build order: {:?}", build_order);
// Build workspace
let builder = WorkspaceBuilder::new(&workspace);
let result = builder.build_all(&BuildConfig::default())?;
if result.success {
println!("Build succeeded!");
}
// Bump versions
let version_mgr = VersionManager::new(&workspace);
let changes = version_mgr.preview_changes(
VersionUpdateStrategy::BumpAll(BumpType::Patch)
)?;
for change in &changes {
println!("{}", change.description());
}
// Apply version changes
version_mgr.apply_changes(&changes)?;
Ok(())
}
Usage Examples
Workspace Discovery
use lmrc_cargo_workspace::workspace::Workspace;
use std::path::Path;
// Discover workspace from current directory
let workspace = Workspace::discover(Path::new("."))?;
// Or load from specific path
let workspace = Workspace::from_path(Path::new("/path/to/workspace"))?;
// Access workspace members
for member in &workspace.members {
println!("Member: {} at {}", member.name, member.path.display());
if member.is_library() {
println!(" Type: Library");
}
if member.is_binary() {
println!(" Type: Binary");
}
}
Dependency Analysis
use lmrc_cargo_workspace::{workspace::Workspace, dependency::DependencyGraph};
let workspace = Workspace::discover(Path::new("."))?;
let graph = DependencyGraph::from_workspace(&workspace)?;
// Get dependencies of a crate
let deps = graph.workspace_dependencies("my-crate");
for dep in deps {
println!("Depends on: {}", dep.name);
}
// Get reverse dependencies (what depends on this crate)
let dependents = graph.dependents("my-lib");
println!("Crates depending on my-lib: {:?}", dependents);
// Get build order (topological sort)
let order = graph.topological_sort()?;
println!("Build in this order: {:?}", order);
// Check for circular dependencies
graph.check_circular_dependencies()?;
Version Management
use lmrc_cargo_workspace::{
workspace::Workspace,
version::{VersionManager, BumpType, VersionUpdateStrategy},
};
use semver::Version;
let workspace = Workspace::discover(Path::new("."))?;
let version_mgr = VersionManager::new(&workspace);
// Bump all crates by patch version
let changes = version_mgr.update_versions(
VersionUpdateStrategy::BumpAll(BumpType::Patch)
)?;
// Or bump specific crates
let mut bumps = std::collections::HashMap::new();
bumps.insert("crate-a".to_string(), BumpType::Major);
bumps.insert("crate-b".to_string(), BumpType::Minor);
let changes = version_mgr.update_versions(
VersionUpdateStrategy::BumpIndividual(bumps)
)?;
// Set all crates to same version
let changes = version_mgr.update_versions(
VersionUpdateStrategy::Unified(Version::new(2, 0, 0))
)?;
// Preview changes before applying
for change in &changes {
println!("{}", change.description());
}
// Apply changes
version_mgr.apply_changes(&changes)?;
Build and Test
use lmrc_cargo_workspace::{
workspace::Workspace,
build::{WorkspaceBuilder, BuildConfig, TestConfig, BuildProfile},
};
let workspace = Workspace::discover(Path::new("."))?;
let builder = WorkspaceBuilder::new(&workspace);
// Build all members in release mode
let mut config = BuildConfig::default();
config.profile = BuildProfile::Release;
config.all_features = true;
let result = builder.build_all(&config)?;
// Build specific member
let result = builder.build_member("my-crate", &config)?;
// Run tests
let mut test_config = TestConfig::default();
test_config.nocapture = true;
let result = builder.test_all(&test_config)?;
// Check code without building
let result = builder.check_all(&BuildConfig::default())?;
Manifest Manipulation
use lmrc_cargo_workspace::manifest::Manifest;
use semver::Version;
use std::path::Path;
let mut manifest = Manifest::from_path(Path::new("Cargo.toml"))?;
// Update package version
manifest.set_package_version(&Version::new(2, 0, 0))?;
// Update dependency version
manifest.update_dependency("serde", "2.0", "dependencies")?;
// Check if it's a workspace
if manifest.is_workspace() {
let members = manifest.workspace_members()?;
println!("Workspace members: {:?}", members);
}
// Save changes (preserves formatting and comments)
manifest.save()?;
Selective Build and Test
use lmrc_cargo_workspace::{
selective::MemberSelector,
build::{BuildConfig, TestConfig},
};
let workspace = Workspace::discover(Path::new("."))?;
// Build only binaries
let results = MemberSelector::new(&workspace)
.binaries_only()
.build(&BuildConfig::default())?;
// Build crates matching a pattern
let results = MemberSelector::new(&workspace)
.by_pattern("*-core")?
.build(&BuildConfig::default())?;
// Test only changed crates since a git ref
let results = MemberSelector::new(&workspace)
.changed_since("main")?
.with_dependents()? // Include crates that depend on changes
.test(&TestConfig::default())?;
// Build in parallel respecting dependency order
let results = MemberSelector::new(&workspace)
.all()
.build_parallel(&BuildConfig::default())?;
Cargo Metadata Integration
use lmrc_cargo_workspace::metadata::CargoMetadata;
let metadata = CargoMetadata::load(&workspace.root)?;
// Check for duplicate dependency versions
let duplicates = metadata.find_duplicate_versions();
for (dep, versions) in duplicates {
println!("{} has versions: {}", dep, versions.join(", "));
}
// Collect license information
let licenses = metadata.collect_licenses();
for (license, packages) in licenses {
println!("{}: {} packages", license, packages.len());
}
// Access resolved dependency information
for package in metadata.workspace_packages() {
println!("{} v{}", package.name, package.version);
for dep in &package.dependencies {
if dep.is_normal() {
println!(" - {} {}", dep.name, dep.req);
}
}
}
Workspace Validation
use lmrc_cargo_workspace::validation::Validator;
let validator = Validator::new(&workspace);
// Check version consistency
let issues = validator.check_version_consistency()?;
// Check for circular dependencies
let issues = validator.check_circular_dependencies()?;
// Check code formatting
let issues = validator.check_formatting()?;
// Check with clippy
let issues = validator.check_clippy()?;
// Run all validations
let result = validator.validate_all()?;
if result.has_errors() {
println!("Errors: {}", result.errors().len());
}
if result.has_warnings() {
println!("Warnings: {}", result.warnings().len());
}
println!("{}", result.summary());
API Documentation
Full API documentation is available at docs.rs.
Modules
workspace: Workspace discovery and member enumerationmanifest: Cargo.toml parsing and manipulation with format preservationdependency: Dependency graph analysis and topological sortingversion: Semver version management and updatesbuild: Programmatic build and test operationsselective: Selective build/test operations based on patterns, changes, or dependenciesmetadata: Cargo metadata integration for resolved dependency informationvalidation: Workspace validation and lintingerror: Comprehensive error types
Error Handling
All operations return Result<T, Error> with detailed error information:
use lmrc_cargo_workspace::{workspace::Workspace, error::Error};
match Workspace::discover(Path::new(".")) {
Ok(workspace) => println!("Found workspace!"),
Err(Error::WorkspaceNotFound(path)) => {
eprintln!("No workspace found at: {}", path.display());
}
Err(Error::InvalidManifest { path, reason }) => {
eprintln!("Invalid manifest at {}: {}", path.display(), reason);
}
Err(e) => eprintln!("Error: {}", e),
}
Requirements
- Rust 2021 edition or later
- Cargo must be installed for build/test operations
Contributing
Contributions are welcome! Please feel free to submit issues and pull requests.
License
Part of the LMRC Stack project. Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Related Projects
- cargo - The Rust package manager
- cargo-edit - CLI tool for managing Cargo dependencies
- cargo-workspace - Workspace management tool
Acknowledgments
This library uses:
Dependencies
~2.7–6MB
~111K SLoC