Skip to main content

PackageManager

Trait PackageManager 

Source
pub trait PackageManager: Send + Sync {
    // Required methods
    fn name(&self) -> &'static str;
    fn detect(&self, project_path: &Path) -> bool;
    fn bloat_dirs(&self, project_path: &Path) -> Vec<BloatDir>;
    fn enforce_lockfile(
        &self,
        project_path: &Path,
        policy: EnforcePolicy,
    ) -> Result<()>;
    fn restore(&self, project_path: &Path, timeout: Duration) -> Result<()>;

    // Provided methods
    fn restore_named(
        &self,
        project_path: &Path,
        dir_name: &str,
        runtime: Option<&str>,
        timeout: Duration,
    ) -> Result<()> { ... }
    fn runtime_tag(&self, project_path: &Path, dir_name: &str) -> Option<String> { ... }
    fn lockfiles(&self) -> &'static [&'static str] { ... }
    fn drift(&self, project_path: &Path) -> Vec<DriftReport> { ... }
    fn opt_in(&self) -> bool { ... }
}
Expand description

The core trait that every package manager adapter must implement.

Each adapter is responsible for:

  • Detecting whether it applies to a given project directory
  • Listing the bloat directories it manages
  • Enforcing lockfile consistency before deletion
  • Restoring dependencies from lockfiles

Required Methods§

Source

fn name(&self) -> &'static str

Human-readable name for this adapter (e.g., “npm”, “pnpm”, “uv”).

Source

fn detect(&self, project_path: &Path) -> bool

Check if this adapter applies to the given project directory.

Typically checks for the presence of a specific lockfile or config file.

Source

fn bloat_dirs(&self, project_path: &Path) -> Vec<BloatDir>

List all bloat directories this adapter manages in the given project.

Only returns directories that actually exist on disk.

Source

fn enforce_lockfile( &self, project_path: &Path, policy: EnforcePolicy, ) -> Result<()>

Prove the lockfile can rebuild what is about to be deleted.

This is a safety-critical method. It MUST succeed before any bloat directory is deleted. If this fails, deletion for this adapter is aborted.

See EnforcePolicy for the one rule every adapter follows.

Source

fn restore(&self, project_path: &Path, timeout: Duration) -> Result<()>

Restore dependencies from the lockfile (for dev-prune restore).

timeout is threaded explicitly for the same reason EnforcePolicy is: the restore path used to burn the compiled-in default regardless of command_timeout_secs, and a full npm ci on a large tree needs the raised timeout far more often than a verify does.

Provided Methods§

Source

fn restore_named( &self, project_path: &Path, dir_name: &str, runtime: Option<&str>, timeout: Duration, ) -> Result<()>

PackageManager::restore, told the name the pruned directory had.

Most managers have exactly one possible directory name and ignore this. venv does not: it prunes any folder carrying a pyvenv.cfgvenv, env, my_env — and without the recorded name it would rebuild the environment as .venv, leaving every activate script, IDE interpreter path and Makefile pointing at nothing. runtime is the interpreter tag recorded when the directory was deleted (see crate::config::PrunedDir::runtime). None means nothing was recorded, or the caller has decided this machine cannot honour it; either way the manager should fall back to whatever it would have used before.

Source

fn runtime_tag(&self, project_path: &Path, dir_name: &str) -> Option<String>

The language runtime a bloat directory is built against, recorded at prune time.

Only the Python managers answer this. A node_modules is rebuilt by the same npm ci whichever Node is installed, and cargo and go pin their toolchains in files that are already in the repository — but a virtual environment is a copy of one specific interpreter, and rebuilding it on a different one silently changes which wheels resolve.

dir_name is the directory about to be deleted, relative to project_path.

Source

fn lockfiles(&self) -> &'static [&'static str]

The file this manager rebuilds its bloat directory from.

Two callers. Conflict resolution breaks ties between managers that share a bloat directory — npm, pnpm, yarn and bun all own the same node_modules — by comparing these files’ timestamps. devp doctor names them, because a missing one is the most common reason a project is not pruneable.

More than one entry means the manager accepts any of them (bun’s binary and text lockfiles). An empty slice means the manager has no single file to point at.

Source

fn drift(&self, project_path: &Path) -> Vec<DriftReport>

Installed-but-unrecorded packages, as data instead of a refusal.

The same comparison PackageManager::enforce_lockfile refuses a prune on, surfaced early so devp status --drift can point at the problem before a prune is ever attempted. Runs nothing and writes nothing. An empty answer means “nothing detected”, not “proven clean” — most managers have no cheap way to compare and say nothing here.

Source

fn opt_in(&self) -> bool

Whether this adapter is inert until the user enables it in settings.

Adapters whose directory is compiler output answer true — cargo, gradle, maven and swift. Theirs come back by recompiling the project, which costs far more than a dependency reinstall, so nobody should find them deleted without having asked. The engine also holds them to the longer build_idle_days idle window.

The test is what it costs to get the directory back, not whether a lockfile exists: cargo has as good a lockfile as npm does, and target/ still has to be rebuilt from source.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§