Skip to content

Instantly share code, notes, and snippets.

@Gankra
Last active April 2, 2022 02:05
Show Gist options
  • Save Gankra/349dcf6732262ed33b2aa216243a75be to your computer and use it in GitHub Desktop.
Save Gankra/349dcf6732262ed33b2aa216243a75be to your computer and use it in GitHub Desktop.
/// Perform crimes on clap long_help to generate markdown docs
fn cmd_help_markdown(
out: &mut dyn Write,
) -> Result<(), Box<dyn Error>> {
// Make a new App to get the help message this time.
writeln!(out, "# cargo-mysubcommand CLI manual")?;
writeln!(out)?;
writeln!(
out,
"> This manual can be regenerated with `cargo mysubcommand help-markdown`"
)?;
writeln!(out)?;
let mut full_command = Cli::command();
let mut todo = vec![&mut full_command];
let mut is_full_command = true;
while let Some(command) = todo.pop() {
let mut help_buf = Vec::new();
command.write_long_help(&mut help_buf).unwrap();
let help = String::from_utf8(help_buf).unwrap();
// First line is --version
let mut lines = help.lines();
let version_line = lines.next().unwrap();
let mut subcommand_name = format!("cargo mysubcommand {} ", command.get_name());
if is_full_command {
writeln!(out, "Version: `{version_line}`")?;
writeln!(out)?;
subcommand_name = String::new();
} else {
// Give subcommands some breathing room
writeln!(out, "<br><br><br>")?;
writeln!(out, "## {}", subcommand_name)?;
}
let mut in_subcommands_listing = false;
for line in lines {
// Use a trailing colon to indicate a heading
if let Some(heading) = line.strip_suffix(':') {
if !line.starts_with(' ') {
// SCREAMING headers are Main headings
if heading.to_ascii_uppercase() == heading {
in_subcommands_listing = heading == "SUBCOMMANDS";
writeln!(out, "### {subcommand_name}{heading}")?;
} else {
writeln!(out, "### {heading}")?;
}
continue;
}
}
if in_subcommands_listing && !line.starts_with(" ") {
// subcommand names are list items
let own_subcommand_name = line.trim();
write!(
out,
"* [{own_subcommand_name}](#cargo-mysubcommand-{own_subcommand_name}): "
)?;
continue;
}
// The rest is indented, get rid of that
let line = line.trim();
// Usage strings get wrapped in full code blocks
if line.starts_with("cargo-mysubcommand ") {
writeln!(out, "```")?;
writeln!(out, "{}", line)?;
writeln!(out, "```")?;
continue;
}
// argument names are subheadings
if line.starts_with('-') || line.starts_with('<') {
writeln!(out, "#### `{}`", line)?;
continue;
}
// escape default/value strings
if line.starts_with('[') {
writeln!(out, "\\{}", line)?;
continue;
}
// Normal paragraph text
writeln!(out, "{}", line)?;
}
writeln!(out)?;
todo.extend(command.get_subcommands_mut());
is_full_command = false;
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment