diff --git a/crates/puffin/src/main.rs b/crates/puffin/src/main.rs index 63a172492..60030a87d 100644 --- a/crates/puffin/src/main.rs +++ b/crates/puffin/src/main.rs @@ -6,6 +6,7 @@ use std::str::FromStr; use anstream::eprintln; use anyhow::Result; use chrono::{DateTime, Days, NaiveDate, NaiveTime, Utc}; +use clap::error::{ContextKind, ContextValue}; use clap::{Args, Parser, Subcommand}; use owo_colors::OwoColorize; use tracing::instrument; @@ -614,7 +615,48 @@ struct RemoveArgs { #[instrument] // Anchor span to check for overhead async fn run() -> Result { - let cli = Cli::parse(); + let cli = match Cli::try_parse() { + Ok(cli) => cli, + Err(mut err) => { + if let Some(ContextValue::String(subcommand)) = err.get(ContextKind::InvalidSubcommand) + { + match subcommand.as_str() { + "compile" | "lock" => { + err.insert( + ContextKind::SuggestedSubcommand, + ContextValue::String("puffin pip compile".to_string()), + ); + } + "sync" => { + err.insert( + ContextKind::SuggestedSubcommand, + ContextValue::String("puffin pip sync".to_string()), + ); + } + "install" | "add" => { + err.insert( + ContextKind::SuggestedSubcommand, + ContextValue::String("puffin pip install".to_string()), + ); + } + "uninstall" | "remove" => { + err.insert( + ContextKind::SuggestedSubcommand, + ContextValue::String("puffin pip uninstall".to_string()), + ); + } + "freeze" => { + err.insert( + ContextKind::SuggestedSubcommand, + ContextValue::String("puffin pip freeze".to_string()), + ); + } + _ => {} + } + } + err.exit() + } + }; // Configure the `tracing` crate, which controls internal logging. #[cfg(feature = "tracing-durations-export")] diff --git a/crates/puffin/tests/pip_sync.rs b/crates/puffin/tests/pip_sync.rs index 9449d8926..99423c4f2 100644 --- a/crates/puffin/tests/pip_sync.rs +++ b/crates/puffin/tests/pip_sync.rs @@ -55,6 +55,24 @@ fn uninstall_command(context: &TestContext) -> Command { command } +#[test] +fn missing_pip() { + puffin_snapshot!(Command::new(get_bin()).arg("sync"), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: unrecognized subcommand 'sync' + + tip: a similar subcommand exists: 'puffin pip sync' + + Usage: puffin [OPTIONS] + + For more information, try '--help'. + "###); +} + #[test] fn missing_requirements_txt() { let context = TestContext::new("3.12");