clap/clap_man/src/lib.rs

136 lines
3.6 KiB
Rust
Raw Normal View History

#![doc(html_logo_url = "https://raw.githubusercontent.com/clap-rs/clap/master/assets/clap.png")]
#![doc = include_str!("../README.md")]
#![warn(missing_docs, trivial_casts, unused_allocation, trivial_numeric_casts)]
#![forbid(unsafe_code)]
#![deny(missing_docs)]
mod render;
pub use roff;
use render::subcommand_heading;
use roff::{roman, Roff};
use std::io::Write;
/// Generate a manual page and write it out.
pub fn generate_manpage<'a>(
app: &mut clap::App<'a>,
buf: &mut dyn Write,
) -> Result<(), std::io::Error> {
let meta = Meta::from_clap("1", "", app);
let man = Man::new(&meta, app);
man.render(buf)
}
/// Metadata about a manual page.
pub struct Meta {
title: String,
section: String,
date: String,
source: String,
manual: String,
}
impl Meta {
/// Create metadata from a clap::App.
pub fn from_clap(section: &str, manual: &str, app: &clap::App) -> Self {
Self {
title: app.get_name().to_string(),
section: section.to_string(),
date: "".to_string(), // FIXME
source: format!(
"{} {}",
app.get_name(),
app.get_version().unwrap_or_default()
),
manual: manual.to_string(),
}
}
// Turn metadata into arguments for a .TH macro.
fn to_args(&self) -> Vec<&str> {
vec![
&self.title,
&self.section,
&self.date,
&self.source,
&self.manual,
]
}
}
/// A manual page as constructed from a clap::App.
pub struct Man {
roff: Roff,
}
impl Man {
/// Create a new manual page.
pub fn new(meta: &Meta, app: &mut clap::App) -> Self {
app._build_all();
let mut roff = Roff::default();
roff.control("TH", meta.to_args());
roff.control("SH", ["NAME"]);
render::about(&mut roff, app);
roff.control("SH", ["SYNOPSIS"]);
render::synopsis(&mut roff, app);
roff.control("SH", ["DESCRIPTION"]);
render::description(&mut roff, app);
if app_has_arguments(app) {
roff.control("SH", ["OPTIONS"]);
render::options(&mut roff, app);
}
if app_has_subcommands(app) {
let heading = subcommand_heading(app);
roff.control("SH", [heading.as_str()]);
render::subcommands(&mut roff, app, &meta.section);
}
if app.get_after_long_help().is_some() || app.get_after_help().is_some() {
roff.control("SH", ["EXTRA"]);
render::after_help(&mut roff, app);
}
if app_has_version(app) {
let version = roman(&render::version(app));
roff.control("SH", ["VERSION"]);
roff.text([version]);
}
if app.get_author().is_some() {
let author = roman(app.get_author().unwrap_or_default());
roff.control("SH", ["AUTHORS"]);
roff.text([author]);
}
Self { roff }
}
/// Render a manual page into writer.
pub fn render(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
self.roff.to_writer(w)
}
}
// Does the application have a version?
fn app_has_version(app: &clap::App) -> bool {
app.get_version()
.or_else(|| app.get_long_version())
.is_some()
}
// Does the application have any command line arguments?
fn app_has_arguments(app: &clap::App) -> bool {
app.get_arguments()
.any(|i| !i.is_set(clap::ArgSettings::Hidden))
}
// Does the application have any subcommands?
fn app_has_subcommands(app: &clap::App) -> bool {
app.get_subcommands()
.any(|i| !i.is_set(clap::AppSettings::Hidden))
}