diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 2519ec317..d30ef3655 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -2014,7 +2014,8 @@ pub struct ToolListArgs; #[derive(Args)] #[allow(clippy::struct_excessive_bools)] pub struct ToolUninstallArgs { - pub name: String, + /// The name of the tool to uninstall. + pub name: PackageName, } #[derive(Args)] diff --git a/crates/uv-tool/src/lib.rs b/crates/uv-tool/src/lib.rs index ecab0edfe..117a7111a 100644 --- a/crates/uv-tool/src/lib.rs +++ b/crates/uv-tool/src/lib.rs @@ -96,8 +96,8 @@ impl InstalledTools { } /// Get the receipt for the given tool. - pub fn get_tool_receipt(&self, name: &str) -> Result, Error> { - let path = self.root.join(name).join("uv-receipt.toml"); + pub fn get_tool_receipt(&self, name: &PackageName) -> Result, Error> { + let path = self.root.join(name.to_string()).join("uv-receipt.toml"); match ToolReceipt::from_path(&path) { Ok(tool_receipt) => Ok(Some(tool_receipt.tool)), Err(Error::IO(err)) if err.kind() == io::ErrorKind::NotFound => Ok(None), @@ -114,8 +114,8 @@ impl InstalledTools { } /// Lock a tool directory. - fn acquire_tool_lock(&self, name: &str) -> Result { - let path = self.root.join(name); + fn acquire_tool_lock(&self, name: &PackageName) -> Result { + let path = self.root.join(name.to_string()); Ok(LockedFile::acquire( path.join(".lock"), path.user_display(), @@ -125,11 +125,11 @@ impl InstalledTools { /// Add a receipt for a tool. /// /// Any existing receipt will be replaced. - pub fn add_tool_receipt(&self, name: &str, tool: Tool) -> Result<(), Error> { + pub fn add_tool_receipt(&self, name: &PackageName, tool: Tool) -> Result<(), Error> { let _lock = self.acquire_tool_lock(name); let tool_receipt = ToolReceipt::from(tool); - let path = self.root.join(name).join("uv-receipt.toml"); + let path = self.root.join(name.to_string()).join("uv-receipt.toml"); debug!( "Adding metadata entry for tool `{name}` at {}", @@ -147,9 +147,9 @@ impl InstalledTools { /// Remove the environment for a tool. /// /// Does not remove the tool's entrypoints. - pub fn remove_environment(&self, name: &str) -> Result<(), Error> { + pub fn remove_environment(&self, name: &PackageName) -> Result<(), Error> { let _lock = self.acquire_lock(); - let environment_path = self.root.join(name); + let environment_path = self.root.join(name.to_string()); debug!( "Deleting environment for tool `{name}` at {}", @@ -161,15 +161,16 @@ impl InstalledTools { Ok(()) } + /// Return the [`PythonEnvironment`] for a given tool. pub fn environment( &self, - name: &str, + name: &PackageName, remove_existing: bool, interpreter: Interpreter, cache: &Cache, ) -> Result { let _lock = self.acquire_lock(); - let environment_path = self.root.join(name); + let environment_path = self.root.join(name.to_string()); if !remove_existing && environment_path.exists() { debug!( diff --git a/crates/uv/src/commands/tool/install.rs b/crates/uv/src/commands/tool/install.rs index 4b985a17f..a1d5e1a31 100644 --- a/crates/uv/src/commands/tool/install.rs +++ b/crates/uv/src/commands/tool/install.rs @@ -122,11 +122,9 @@ pub(crate) async fn install( .unwrap() }; - let name = from.name.to_string(); - let installed_tools = InstalledTools::from_settings()?; - let existing_tool_receipt = installed_tools.get_tool_receipt(&name)?; + let existing_tool_receipt = installed_tools.get_tool_receipt(&from.name)?; // TODO(zanieb): Automatically replace an existing tool if the request differs let reinstall_entry_points = if existing_tool_receipt.is_some() { if force { @@ -142,7 +140,11 @@ pub(crate) async fn install( Reinstall::Packages(ref packages) => packages.contains(&from.name), // If not reinstalling... then we're done Reinstall::None => { - writeln!(printer.stderr(), "Tool `{name}` is already installed")?; + writeln!( + printer.stderr(), + "Tool `{}` is already installed", + from.name + )?; return Ok(ExitStatus::Failure); } } @@ -176,7 +178,7 @@ pub(crate) async fn install( // TODO(zanieb): Build the environment in the cache directory then copy into the tool directory // This lets us confirm the environment is valid before removing an existing install let environment = installed_tools.environment( - &name, + &from.name, // Do not remove the existing environment if we're reinstalling a subset of packages !matches!(settings.reinstall, Reinstall::Packages(_)), interpreter, @@ -208,7 +210,11 @@ pub(crate) async fn install( // Exit early if we're not supposed to be reinstalling entry points // e.g. `--reinstall-package` was used for some dependency if existing_tool_receipt.is_some() && !reinstall_entry_points { - writeln!(printer.stderr(), "Updated environment for tool `{name}`")?; + writeln!( + printer.stderr(), + "Updated environment for tool `{}`", + from.name + )?; return Ok(ExitStatus::Success); } @@ -246,9 +252,9 @@ pub(crate) async fn install( if target_entry_points.is_empty() { // Clean up the environment we just created - installed_tools.remove_environment(&name)?; + installed_tools.remove_environment(&from.name)?; - bail!("No entry points found for tool `{name}`"); + bail!("No entry points found for tool `{}`", from.name); } // Check if they exist, before installing @@ -266,7 +272,7 @@ pub(crate) async fn install( } } else if existing_entry_points.peek().is_some() { // Clean up the environment we just created - installed_tools.remove_environment(&name)?; + installed_tools.remove_environment(&from.name)?; let existing_entry_points = existing_entry_points // SAFETY: We know the target has a filename because we just constructed it above @@ -300,7 +306,7 @@ pub(crate) async fn install( .join(", ") )?; - debug!("Adding receipt for tool `{name}`"); + debug!("Adding receipt for tool `{}`", from.name); let installed_tools = installed_tools.init()?; let tool = Tool::new( requirements @@ -312,7 +318,7 @@ pub(crate) async fn install( .into_iter() .map(|(name, _, target_path)| ToolEntrypoint::new(name, target_path)), ); - installed_tools.add_tool_receipt(&name, tool)?; + installed_tools.add_tool_receipt(&from.name, tool)?; Ok(ExitStatus::Success) } diff --git a/crates/uv/src/commands/tool/uninstall.rs b/crates/uv/src/commands/tool/uninstall.rs index bfd2eb3f6..1740b3d0d 100644 --- a/crates/uv/src/commands/tool/uninstall.rs +++ b/crates/uv/src/commands/tool/uninstall.rs @@ -6,6 +6,7 @@ use itertools::Itertools; use tracing::debug; use uv_configuration::PreviewMode; use uv_fs::Simplified; +use uv_normalize::PackageName; use uv_tool::InstalledTools; use uv_warnings::warn_user_once; @@ -14,7 +15,7 @@ use crate::printer::Printer; /// Uninstall a tool. pub(crate) async fn uninstall( - name: String, + name: PackageName, preview: PreviewMode, printer: Printer, ) -> Result { diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index 8f7ec32fa..d2d956a41 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -300,7 +300,7 @@ impl ToolListSettings { #[allow(clippy::struct_excessive_bools)] #[derive(Debug, Clone)] pub(crate) struct ToolUninstallSettings { - pub(crate) name: String, + pub(crate) name: PackageName, } impl ToolUninstallSettings {