| //! Automatically attempt to fix vulnerable dependencies | 
 | //! | 
 | //! This module is **experimental**, and its behavior may change in the future. | 
 |  | 
 | use crate::vulnerability::Vulnerability; | 
 | use cargo_lock::{Lockfile, Package}; | 
 | use std::path::{Path, PathBuf}; | 
 | use std::process::Command; | 
 |  | 
 | /// Auto-fixer for vulnerable dependencies | 
 | #[cfg_attr(docsrs, doc(cfg(feature = "fix")))] | 
 | pub struct Fixer { | 
 |     lockfile: Lockfile, | 
 |     manifest_path: Option<PathBuf>, | 
 |     path_to_cargo: Option<PathBuf>, | 
 | } | 
 |  | 
 | impl Fixer { | 
 |     /// Create a new [`Fixer`] for the given `Cargo.lock` file | 
 |     /// | 
 |     /// `path_to_cargo` defaults to `cargo`, resolved in your `$PATH`. | 
 |     /// | 
 |     /// If the path to `Cargo.toml` is not specified, the `cargo update` command | 
 |     /// will be run in the directory with the `Cargo.lock` file. | 
 |     /// Leaving it blank will fix the entire workspace. | 
 |     pub fn new( | 
 |         cargo_lock: Lockfile, | 
 |         cargo_toml: Option<PathBuf>, | 
 |         path_to_cargo: Option<PathBuf>, | 
 |     ) -> Self { | 
 |         Self { | 
 |             lockfile: cargo_lock, | 
 |             manifest_path: cargo_toml, | 
 |             path_to_cargo, | 
 |         } | 
 |     } | 
 |  | 
 |     /// Returns a command that calls `cargo update` with the right arguments | 
 |     /// to attempt to fix this vulnerability. | 
 |     /// | 
 |     /// Note that the success of the command does not mean | 
 |     /// the vulnerability was actually fixed! | 
 |     /// It may remain if no semver-compatible fix was available. | 
 |     pub fn get_fix_command(&self, vulnerability: &Vulnerability, dry_run: bool) -> Command { | 
 |         let cargo_path: &Path = self.path_to_cargo.as_deref().unwrap_or(Path::new("cargo")); | 
 |         let pkg_name = &vulnerability.package.name; | 
 |         let mut command = Command::new(cargo_path); | 
 |         command.arg("update"); | 
 |         if let Some(path) = self.manifest_path.as_ref() { | 
 |             command.arg("--manifest-path").arg(path); | 
 |         } | 
 |         if dry_run { | 
 |             command.arg("--dry-run"); | 
 |         } | 
 |         // there can be more than one version of a given package in the lockfile, so we need to iterate over all of them | 
 |         for pkg in self.lockfile.packages.iter().filter(|pkg| { | 
 |             &pkg.name == pkg_name && vulnerability.versions.is_vulnerable(&pkg.version) | 
 |         }) { | 
 |             let pkgid = pkgid(pkg); | 
 |             command.arg(&pkgid); | 
 |         } | 
 |  | 
 |         command | 
 |     } | 
 | } | 
 |  | 
 | /// Returns a Cargo unique identifier for a package. | 
 | /// See `cargo help pkgid` for more info. | 
 | /// | 
 | /// We need to pass these to `cargo update` because otherwise | 
 | /// the package specification will be ambiguous, and it will refuse to do anything. | 
 | fn pkgid(pkg: &Package) -> String { | 
 |     match pkg.source.as_ref() { | 
 |         Some(source) => format!("{}#{}@{}", source, pkg.name, pkg.version), | 
 |         None => format!("{}@{}", pkg.name, pkg.version), | 
 |     } | 
 | } |