diff --git a/crates/pile/src/command/mod.rs b/crates/pile/src/command/mod.rs index d3a09a1..d4bfa0b 100644 --- a/crates/pile/src/command/mod.rs +++ b/crates/pile/src/command/mod.rs @@ -1,6 +1,8 @@ use anyhow::Result; -use clap::Subcommand; -use pile_toolbox::cancelabletask::{CancelFlag, CancelableTask, CancelableTaskError}; +use clap::{CommandFactory, Subcommand}; +use pile_toolbox::cancelabletask::{ + CancelFlag, CancelableTask, CancelableTaskError, CancelableTaskResult, +}; mod annotate; mod check; @@ -9,10 +11,14 @@ mod init; mod lookup; mod probe; -use crate::GlobalContext; +use crate::{Cli, GlobalContext}; #[derive(Debug, Subcommand)] pub enum SubCommand { + /// Print help for all subcommands + #[clap(alias = "doc")] + Docs {}, + /// Annotate all items with a field, writing it to a sidecar path Annotate { #[command(flatten)] @@ -59,6 +65,13 @@ impl CliCmdDispatch for SubCommand { Self::Index { cmd } => cmd.start(ctx), Self::Lookup { cmd } => cmd.start(ctx), Self::Probe { cmd } => cmd.start(ctx), + + Self::Docs {} => { + print_help_recursively(&mut Cli::command(), None); + return Ok(CancelableTask::spawn(|_| async { + CancelableTaskResult::Finished(Ok(0)) + })); + } } } } @@ -80,3 +93,26 @@ pub(super) trait CliCmd: Sized + Send + 'static { flag: CancelFlag, ) -> impl std::future::Future>> + Send; } + +#[expect(clippy::print_stdout)] +fn print_help_recursively(command: &mut clap::Command, parent: Option<&str>) { + if command.get_name() == "help" || command.get_name() == "docs" { + return; + } + + let parent = match parent { + None => command.get_name().into(), + Some(parent) => format!("{parent} {}", command.get_name()), + }; + + let b = "=".repeat(parent.len()); + println!("============{b}==="); + println!("# Docs for `{parent}` #"); + println!("============{b}==="); + println!("{}", command.render_help()); + + for subcommand in command.get_subcommands_mut() { + println!(); + print_help_recursively(subcommand, Some(&parent)); + } +}