diff --git a/crates/puffin-cli/src/commands/add.rs b/crates/puffin-cli/src/commands/add.rs index d46c182a6..12ae3b5f0 100644 --- a/crates/puffin-cli/src/commands/add.rs +++ b/crates/puffin-cli/src/commands/add.rs @@ -38,11 +38,11 @@ enum AddError { InvalidRequirement(String, #[source] pep508_rs::Pep508Error), #[error("Failed to parse `pyproject.toml` at: `{0}`")] - #[diagnostic(code(puffin::add::parse_error))] + #[diagnostic(code(puffin::add::parse))] ParseError(PathBuf, #[source] WorkspaceError), #[error("Failed to write `pyproject.toml` to: `{0}`")] - #[diagnostic(code(puffin::add::write_error))] + #[diagnostic(code(puffin::add::write))] WriteError(PathBuf, #[source] WorkspaceError), } diff --git a/crates/puffin-cli/src/commands/remove.rs b/crates/puffin-cli/src/commands/remove.rs index 965b0b9e9..d020baf45 100644 --- a/crates/puffin-cli/src/commands/remove.rs +++ b/crates/puffin-cli/src/commands/remove.rs @@ -34,15 +34,15 @@ enum RemoveError { WorkspaceNotFound, #[error("Failed to parse `pyproject.toml` at: `{0}`")] - #[diagnostic(code(puffin::remove::parse_error))] + #[diagnostic(code(puffin::remove::parse))] ParseError(PathBuf, #[source] WorkspaceError), #[error("Failed to write `pyproject.toml` to: `{0}`")] - #[diagnostic(code(puffin::remove::write_error))] + #[diagnostic(code(puffin::remove::write))] WriteError(PathBuf, #[source] WorkspaceError), #[error("Failed to remove `{0}` from `pyproject.toml`")] - #[diagnostic(code(puffin::remove::parse_error))] + #[diagnostic(code(puffin::remove::parse))] RemovalError(String, #[source] WorkspaceError), } diff --git a/crates/puffin-cli/src/commands/venv.rs b/crates/puffin-cli/src/commands/venv.rs index 6a66c8db7..bc855e0d4 100644 --- a/crates/puffin-cli/src/commands/venv.rs +++ b/crates/puffin-cli/src/commands/venv.rs @@ -3,43 +3,86 @@ use std::path::Path; use anyhow::Result; use colored::Colorize; -use fs_err::tokio as fs; +use fs_err as fs; +use miette::{Diagnostic, IntoDiagnostic}; +use thiserror::Error; use crate::commands::ExitStatus; use crate::printer::Printer; /// Create a virtual environment. -pub(crate) async fn venv( +#[allow(clippy::unnecessary_wraps)] +pub(crate) fn venv( + path: &Path, + base_python: Option<&Path>, + printer: Printer, +) -> Result { + match venv_impl(path, base_python, printer) { + Ok(status) => Ok(status), + Err(err) => { + #[allow(clippy::print_stderr)] + { + eprint!("{err:?}"); + } + Ok(ExitStatus::Failure) + } + } +} + +#[derive(Error, Debug, Diagnostic)] +enum VenvError { + #[error("Unable to find a Python interpreter")] + #[diagnostic(code(puffin::venv::python_not_found))] + PythonNotFound, + + #[error("Failed to extract Python interpreter info")] + #[diagnostic(code(puffin::venv::interpreter))] + InterpreterError(#[source] gourgeist::Error), + + #[error("Failed to create virtual environment")] + #[diagnostic(code(puffin::venv::creation))] + CreationError(#[source] gourgeist::Error), +} + +/// Create a virtual environment. +fn venv_impl( path: &Path, base_python: Option<&Path>, mut printer: Printer, -) -> Result { +) -> miette::Result { // Locate the Python interpreter. // TODO(charlie): Look at how Maturin discovers and ranks all the available Python interpreters. let base_python = if let Some(base_python) = base_python { base_python.to_path_buf() } else { - which::which("python3").or_else(|_| which::which("python"))? + which::which("python3") + .or_else(|_| which::which("python")) + .map_err(|_| VenvError::PythonNotFound)? }; - let interpreter_info = gourgeist::get_interpreter_info(&base_python)?; + let interpreter_info = + gourgeist::get_interpreter_info(&base_python).map_err(VenvError::InterpreterError)?; + writeln!( printer, "Using Python interpreter: {}", format!("{}", base_python.display()).cyan() - )?; + ) + .into_diagnostic()?; // If the path already exists, remove it. - fs::remove_file(path).await.ok(); - fs::remove_dir_all(path).await.ok(); + fs::remove_file(path).ok(); + fs::remove_dir_all(path).ok(); writeln!( printer, "Creating virtual environment at: {}", format!("{}", path.display()).cyan() - )?; + ) + .into_diagnostic()?; // Create the virtual environment. - gourgeist::create_venv(path, &base_python, &interpreter_info, true)?; + gourgeist::create_venv(path, &base_python, &interpreter_info, true) + .map_err(VenvError::CreationError)?; Ok(ExitStatus::Success) } diff --git a/crates/puffin-cli/src/main.rs b/crates/puffin-cli/src/main.rs index 556f60c59..65ba46aa7 100644 --- a/crates/puffin-cli/src/main.rs +++ b/crates/puffin-cli/src/main.rs @@ -187,7 +187,7 @@ async fn main() -> ExitCode { } Commands::Clean => commands::clean(cache_dir, printer), Commands::Freeze => commands::freeze(cache_dir, printer), - Commands::Venv(args) => commands::venv(&args.name, args.python.as_deref(), printer).await, + Commands::Venv(args) => commands::venv(&args.name, args.python.as_deref(), printer), Commands::Add(args) => commands::add(&args.name, printer), Commands::Remove(args) => commands::remove(&args.name, printer), }; diff --git a/crates/puffin-cli/tests/snapshots/remove__missing_dependencies_array.snap b/crates/puffin-cli/tests/snapshots/remove__missing_dependencies_array.snap index 7ddf13646..a50fcf3d2 100644 --- a/crates/puffin-cli/tests/snapshots/remove__missing_dependencies_array.snap +++ b/crates/puffin-cli/tests/snapshots/remove__missing_dependencies_array.snap @@ -11,7 +11,7 @@ exit_code: 1 ----- stdout ----- ----- stderr ----- -puffin::remove::parse_error +puffin::remove::parse × Failed to remove `flask` from `pyproject.toml` ╰─▶ no `[project.dependencies]` array found in `pyproject.toml` diff --git a/crates/puffin-cli/tests/snapshots/remove__missing_dependency.snap b/crates/puffin-cli/tests/snapshots/remove__missing_dependency.snap index c42ee9c54..d87d7190d 100644 --- a/crates/puffin-cli/tests/snapshots/remove__missing_dependency.snap +++ b/crates/puffin-cli/tests/snapshots/remove__missing_dependency.snap @@ -11,7 +11,7 @@ exit_code: 1 ----- stdout ----- ----- stderr ----- -puffin::remove::parse_error +puffin::remove::parse × Failed to remove `requests` from `pyproject.toml` ╰─▶ unable to find package: `requests` diff --git a/crates/puffin-cli/tests/snapshots/remove__missing_project_table.snap b/crates/puffin-cli/tests/snapshots/remove__missing_project_table.snap index 5720895ad..f3d1b0ae0 100644 --- a/crates/puffin-cli/tests/snapshots/remove__missing_project_table.snap +++ b/crates/puffin-cli/tests/snapshots/remove__missing_project_table.snap @@ -11,7 +11,7 @@ exit_code: 1 ----- stdout ----- ----- stderr ----- -puffin::remove::parse_error +puffin::remove::parse × Failed to remove `flask` from `pyproject.toml` ╰─▶ no `[project]` table found in `pyproject.toml`