lmrc-cargo-workspace

Crates.iolmrc-cargo-workspace
lib.rslmrc-cargo-workspace
version0.3.16
created_at2025-11-26 17:43:56.420383+00
updated_at2025-12-11 13:27:54.433896+00
descriptionCargo workspace management library for the LMRC Stack - comprehensive library for managing Cargo workspaces programmatically
homepagehttps://gitlab.com/lemarco/lmrc-stack/tree/main/libs/cargo-workspace-manager
repositoryhttps://gitlab.com/lemarco/lmrc-stack
max_upload_size
id1951913
size156,880
Le Marc (lemarco)

documentation

https://docs.rs/lmrc-cargo-workspace

README

lmrc-cargo-workspace

Part of the LMRC Stack - Infrastructure-as-Code toolkit for building production-ready Rust applications

Crates.io Documentation License

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 enumeration
  • manifest: Cargo.toml parsing and manipulation with format preservation
  • dependency: Dependency graph analysis and topological sorting
  • version: Semver version management and updates
  • build: Programmatic build and test operations
  • selective: Selective build/test operations based on patterns, changes, or dependencies
  • metadata: Cargo metadata integration for resolved dependency information
  • validation: Workspace validation and linting
  • error: 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:

at your option.

Related Projects

Acknowledgments

This library uses:

  • toml_edit for format-preserving TOML parsing
  • semver for semantic versioning
  • walkdir for filesystem traversal
Commit count: 0

cargo fmt