use clap::{Args, Command};
use termimad::crossterm::style::Stylize;
use crate::{DocRegistry, HelpPage, HelpTheme};
#[derive(Args, Debug, Clone)]
pub struct HelpArgs {
pub topic: Vec<String>,
}
pub enum HelpTarget<'a> {
Command { path: String, cmd: &'a Command },
Guide { path: String },
Program { cmd: &'a Command },
}
pub fn resolve_help<'a>(root: &'a Command, topic: &[String]) -> anyhow::Result<HelpTarget<'a>> {
let mut cmd = root;
let mut path = Vec::new();
if topic.is_empty() {
return Ok(HelpTarget::Program { cmd });
}
for segment in topic {
if segment == "guide" {
return Ok(HelpTarget::Guide {
path: path.join("."),
});
}
cmd = cmd
.get_subcommands()
.find(|c| c.get_name() == segment)
.ok_or_else(|| anyhow::anyhow!("Unknown help topic"))?;
path.push(segment.clone());
}
Ok(HelpTarget::Command {
path: path.join("."),
cmd,
})
}
pub fn help_command(
app_name: &str,
app_version: Option<&str>,
root: &Command,
theme: &HelpTheme,
args: &HelpArgs,
) -> anyhow::Result<()> {
run_help_topic(app_name, app_version, root, &DocRegistry::new(), theme, &args.topic)
}
pub fn help_command_docs(
app_name: &str,
app_version: Option<&str>,
root: &Command,
docs: &DocRegistry,
theme: &HelpTheme,
args: &HelpArgs,
) -> anyhow::Result<()> {
run_help_topic(app_name, app_version, root, docs, theme, &args.topic)
}
pub fn help_command_program(
app_name: &str,
app_version: Option<&str>,
root: &Command,
theme: &HelpTheme,
) -> anyhow::Result<()> {
run_help_topic(app_name, app_version, root, &DocRegistry::new(), theme, &Vec::new())
}
pub fn help_command_program_docs(
app_name: &str,
app_version: Option<&str>,
root: &Command,
docs: &DocRegistry,
theme: &HelpTheme,
) -> anyhow::Result<()> {
run_help_topic(app_name, app_version, root, docs, theme, &Vec::new())
}
pub fn run_help_topic(
app_name: &str,
app_version: Option<&str>,
root: &Command,
docs: &DocRegistry,
theme: &HelpTheme,
topic: &[String],
) -> anyhow::Result<()> {
let target = resolve_help(root, topic)?;
match target {
HelpTarget::Command { path, cmd } => {
let page = HelpPage::from_clap(
std::env::current_exe()
.expect("Failed to get executable path")
.file_name()
.expect("Failed to get executable name")
.to_str()
.unwrap(),
app_version,
&path,
cmd,
)
.with_docs(docs.command(&path));
crate::render_command_help(theme, &page);
}
HelpTarget::Guide { path } => {
let path = if path.is_empty() {
app_name.to_string()
} else {
path
};
if let Some(guide) = docs.guide(&path) {
let (_, rows) = termimad::crossterm::terminal::size().unwrap();
if guide.lines().count() > rows.into() {
crate::run_scrollable_help(theme, app_name, guide.to_string())?;
} else {
println!("{}", theme.skin.term_text(&guide));
}
} else {
println!(
"Guide for {} was {}.",
&path.with(theme.accent).bold(),
"not found".red().bold()
)
}
}
HelpTarget::Program { cmd } => {
let page = HelpPage::from_clap(app_name, app_version, "", cmd)
.with_docs(docs.command(""));
crate::render_command_help(theme, &page);
}
}
Ok(())
}