cuenv_workspaces/lib.rs
1//! Workspace and dependency resolution for cuenv across multiple package managers.
2//!
3//! This crate provides trait-based abstractions for discovering, parsing, and resolving
4//! workspace configurations and dependencies across different package managers including
5//! npm, Bun, pnpm, Yarn (Classic and Modern), and Cargo.
6//!
7//! # Architecture
8//!
9//! The crate is built around three core traits:
10//!
11//! - [`WorkspaceDiscovery`] - Discovers workspace configuration from a root directory
12//! - [`LockfileParser`] - Parses lockfiles into structured entries
13//! - [`DependencyResolver`] - Builds dependency graphs from workspace and lockfile data
14//!
15//! # Workspace Discovery
16//!
17//! The discovery module provides implementations for finding workspace members from
18//! configuration files.
19//!
20//! ## Feature flags
21//!
22//! ### Discovery Features
23//!
24//! - `discovery-javascript` - Enables npm, Bun, Yarn, and pnpm workspace discovery (**enabled by default**)
25//! - `discovery-rust` - Enables Cargo workspace discovery
26//!
27//! ### Fine-grained Discovery Features
28//!
29//! For minimal dependency footprint:
30//! - `discovery-package-json` - npm/Bun/Yarn discovery via `package.json`
31//! - `discovery-pnpm` - pnpm discovery via `pnpm-workspace.yaml`
32//! - `discovery-cargo` - Cargo discovery via `Cargo.toml`
33//!
34//! ### Default Features
35//!
36//! The crate enables the following features by default:
37//! - `detection` - Package manager detection from lockfiles and commands
38//! - `parsers-javascript` - All JavaScript lockfile parsers
39//! - `discovery-javascript` - All JavaScript workspace discoveries
40//!
41//! This provides a complete out-of-the-box experience for JavaScript/TypeScript projects.
42//! To minimize dependencies for Rust-only or specialized use cases, disable default features:
43//!
44//! ```toml
45//! [dependencies]
46//! cuenv-workspaces = { version = "...", default-features = false, features = ["discovery-rust"] }
47//! ```
48//!
49//! ## Discovery Behavior for Edge Cases
50//!
51//! ### Cargo (`CargoTomlDiscovery`)
52//!
53//! - **Missing `[workspace]` section**: Treated as a valid empty workspace (single-package
54//! repository). Discovery succeeds with zero members.
55//! - **Missing or malformed member manifests**: Silently skipped during member enumeration.
56//! Only valid, parseable members are included in the result.
57//!
58//! ### JavaScript (`PackageJsonDiscovery`, `PnpmWorkspaceDiscovery`)
59//!
60//! - **Missing or malformed member manifests**: Silently skipped during member enumeration.
61//! Only valid, parseable members with a `name` field are included in the result.
62//!
63//! This tolerant behavior ensures that discovery does not fail due to individual member
64//! issues, allowing partial workspace analysis to proceed.
65//!
66//! ## Usage examples
67//!
68//! ```rust,ignore
69//! use cuenv_workspaces::{PackageJsonDiscovery, WorkspaceDiscovery};
70//! use std::path::Path;
71//!
72//! let root = Path::new(".");
73//! let discovery = PackageJsonDiscovery;
74//!
75//! if let Ok(workspace) = discovery.discover(root) {
76//! println!("Found workspace with {} members", workspace.member_count());
77//! }
78//! ```
79//!
80//! # Example
81//!
82//! ```rust,ignore
83//! use cuenv_workspaces::{WorkspaceDiscovery, PackageManager, Workspace};
84//! use std::path::Path;
85//!
86//! // Discover a workspace
87//! let root = Path::new("/path/to/workspace");
88//! let workspace = some_discovery_impl.discover(root)?;
89//!
90//! // Access workspace information
91//! println!("Found {} members", workspace.member_count());
92//! for member in &workspace.members {
93//! println!(" - {} at {}", member.name, member.path.display());
94//! }
95//! ```
96//!
97//! # Core Types
98//!
99//! - [`Workspace`] - Represents a discovered workspace with all its members
100//! - [`WorkspaceMember`] - Represents a single package/crate in the workspace
101//! - [`PackageManager`] - Identifies the package manager in use
102//! - [`DependencySpec`] - Describes how a dependency is specified
103//! - [`LockfileEntry`] - Represents a resolved dependency from a lockfile
104//!
105//! # Package Manager Detection
106//!
107//! The detection module provides automatic package manager detection by scanning
108//! for lockfiles and workspace configurations:
109//!
110//! ```rust,ignore
111//! use cuenv_workspaces::detect_package_managers;
112//! use std::path::Path;
113//!
114//! let root = Path::new("/path/to/workspace");
115//! let managers = detect_package_managers(root)?;
116//!
117//! for manager in managers {
118//! println!("Detected: {}", manager);
119//! }
120//! ```
121//!
122//! You can also detect package managers from command strings:
123//!
124//! ```rust,ignore
125//! use cuenv_workspaces::detect_from_command;
126//!
127//! if let Some(manager) = detect_from_command("cargo build") {
128//! println!("Command uses: {}", manager);
129//! }
130//! ```
131//!
132//! # Lockfile Parsers
133//!
134//! The parsers module provides implementations for parsing various package manager
135//! lockfiles. Each parser is gated behind a feature flag to minimize binary size.
136//!
137//! ## Recommended feature flags
138//!
139//! Use these aggregate features to enable all parsers for an ecosystem:
140//!
141//! - `parsers-javascript` - Enables all JavaScript parsers (npm, bun, pnpm, yarn classic, yarn modern)
142//! - `parsers-rust` - Enables all Rust parsers (currently only Cargo)
143//!
144//! ## Fine-grained feature flags
145//!
146//! For minimal dependency footprint, you can enable individual parsers:
147//!
148//! - `parser-npm` - npm's `package-lock.json` (v3)
149//! - `parser-bun` - Bun's `bun.lock` (JSONC format)
150//! - `parser-pnpm` - pnpm's `pnpm-lock.yaml`
151//! - `parser-yarn-classic` - Yarn Classic (v1.x) `yarn.lock`
152//! - `parser-yarn-modern` - Yarn Modern (v2+) `yarn.lock`
153//! - `parser-cargo` - Cargo's `Cargo.lock`
154//!
155//! ## Usage examples
156//!
157//! ```rust,ignore
158//! use cuenv_workspaces::{NpmLockfileParser, LockfileParser};
159//! use std::path::Path;
160//!
161//! let parser = NpmLockfileParser;
162//! let entries = parser.parse(Path::new("package-lock.json"))?;
163//!
164//! for entry in entries {
165//! println!("{} @ {}", entry.name, entry.version);
166//! }
167//! ```
168//!
169//! ```rust,ignore
170//! use cuenv_workspaces::{CargoLockfileParser, LockfileParser};
171//! use std::path::Path;
172//!
173//! let parser = CargoLockfileParser;
174//! let entries = parser.parse(Path::new("Cargo.lock"))?;
175//!
176//! for entry in entries {
177//! println!("{} @ {}", entry.name, entry.version);
178//! }
179//! ```
180
181#![warn(missing_docs)]
182#![warn(clippy::all, clippy::pedantic)]
183
184pub mod core;
185pub mod error;
186pub mod materializer;
187pub mod resolver;
188
189#[cfg(feature = "detection")]
190pub mod detection;
191
192#[cfg(any(
193 feature = "parsers-javascript",
194 feature = "parsers-rust",
195 feature = "parser-cargo"
196))]
197pub mod parsers;
198
199#[cfg(any(
200 feature = "discovery-package-json",
201 feature = "discovery-pnpm",
202 feature = "discovery-cargo"
203))]
204pub mod discovery;
205
206// Re-export core types
207pub use core::{
208 DependencyRef, DependencySource, DependencySpec, LockfileEntry, PackageManager, Version,
209 VersionReq, Workspace, WorkspaceMember,
210};
211
212// Re-export traits
213pub use core::{DependencyResolver, LockfileParser, WorkspaceDiscovery};
214
215// Re-export resolver types
216pub use resolver::GenericDependencyResolver;
217
218// Re-export error types
219pub use error::{Error, Result};
220
221// Re-export detection functions
222#[cfg(feature = "detection")]
223pub use detection::{detect_from_command, detect_package_managers, detect_with_command_hint};
224
225// Re-export JavaScript discovery types
226#[cfg(feature = "discovery-package-json")]
227pub use discovery::PackageJsonDiscovery;
228
229#[cfg(feature = "discovery-pnpm")]
230pub use discovery::PnpmWorkspaceDiscovery;
231
232// Re-export Rust discovery types
233#[cfg(feature = "discovery-cargo")]
234pub use discovery::CargoTomlDiscovery;
235
236// Re-export JavaScript parser types
237#[cfg(feature = "parser-bun")]
238pub use parsers::javascript::BunLockfileParser;
239#[cfg(feature = "parser-npm")]
240pub use parsers::javascript::NpmLockfileParser;
241#[cfg(feature = "parser-pnpm")]
242pub use parsers::javascript::PnpmLockfileParser;
243#[cfg(feature = "parser-yarn-classic")]
244pub use parsers::javascript::YarnClassicLockfileParser;
245#[cfg(feature = "parser-yarn-modern")]
246pub use parsers::javascript::YarnModernLockfileParser;
247// Re-export Rust parser types
248#[cfg(any(feature = "parsers-rust", feature = "parser-cargo"))]
249pub use parsers::rust::CargoLockfileParser;