fix(man)!: Clean up Meta API

`Meta` wasn't really serving a distinct purpose in the API, so rolled it
into `Man` now that it lazily renders.
This commit is contained in:
Ed Page 2022-01-28 16:21:14 -06:00
parent 1e2bafe78c
commit 2fdb9a3d54
4 changed files with 64 additions and 81 deletions

View file

@ -1,34 +0,0 @@
use clap::{arg, App};
use clap_man::{Man, Meta};
// Run this example as `cargo run --example man-builder | man -l -`.
fn main() -> Result<(), std::io::Error> {
let app = App::new("myapp")
.version("1.0")
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.subcommand_help_heading("Commands")
.arg(
arg!(-c --config <FILE> "Sets a custom config file")
.long_help("Some more text about how to set a custom config file")
.required(false)
.takes_value(true),
)
.arg(arg!([output] "Sets an optional output file").index(1))
.arg(arg!(-d --debug ... "Turn debugging information on"))
.subcommand(
App::new("test")
.about("does testing things")
.arg(arg!(-l --list "Lists test values")),
)
.after_help(
"\
For more information about the config file syntax, look up the INI format.
To see the debug information, visit our website on github.com",
);
let meta = Meta::from_clap("1", "GNU", &app);
let man = Man::new(meta, app);
man.render(&mut std::io::stdout())
}

View file

@ -1,5 +1,5 @@
use clap::{arg, App};
use clap_man::generate_manpage;
use clap_man::Man;
use std::io;
// Run this example as `cargo run --example man | man -l -`.
@ -36,5 +36,5 @@ And a few newlines.",
.arg(arg!(-l --list "Lists test values")),
);
generate_manpage(app, &mut io::stdout())
Man::new(app).render(&mut io::stdout())
}

View file

@ -12,15 +12,9 @@ use render::subcommand_heading;
use roff::{roman, Roff};
use std::io::Write;
/// Generate a manual page and write it out.
pub fn generate_manpage(app: clap::App<'_>, 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 {
/// A manual page as constructed from a clap::App.
pub struct Man<'a> {
app: clap::App<'a>,
title: String,
section: String,
date: String,
@ -28,51 +22,63 @@ pub struct Meta {
manual: String,
}
impl Meta {
/// Create metadata from a clap::App.
pub fn from_clap(section: &str, manual: &str, app: &clap::App) -> Self {
impl<'a> Man<'a> {
/// Create a new manual page.
pub fn new(mut app: clap::App<'a>) -> Self {
app._build_all();
let title = app.get_name().to_owned();
let section = "1".to_owned();
let date = "".to_owned();
let source = format!(
"{} {}",
app.get_name(),
app.get_version().unwrap_or_default()
);
let manual = "".to_owned();
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(),
app,
title,
section,
date,
source,
manual,
}
}
// 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,
]
/// Override the default title
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
}
/// A manual page as constructed from a clap::App.
pub struct Man<'a> {
meta: Meta,
app: clap::App<'a>,
}
/// Override the default section
pub fn section(mut self, section: impl Into<String>) -> Self {
self.section = section.into();
self
}
impl<'a> Man<'a> {
/// Create a new manual page.
pub fn new(meta: Meta, mut app: clap::App<'a>) -> Self {
app._build_all();
Self { meta, app }
/// Override the default date
pub fn date(mut self, date: impl Into<String>) -> Self {
self.date = date.into();
self
}
/// Override the default source
pub fn source(mut self, source: impl Into<String>) -> Self {
self.source = source.into();
self
}
/// Override the default manual
pub fn manual(mut self, manual: impl Into<String>) -> Self {
self.manual = manual.into();
self
}
/// Render a manual page into writer.
pub fn render(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
let mut roff = Roff::default();
roff.control("TH", self.meta.to_args());
roff.control("TH", self.title_args());
self._render_name_section(&mut roff);
self._render_synopsis_section(&mut roff);
self._render_description_section(&mut roff);
@ -100,6 +106,17 @@ impl<'a> Man<'a> {
roff.to_writer(w)
}
// Turn metadata into arguments for a .TH macro.
fn title_args(&self) -> Vec<&str> {
vec![
&self.title,
&self.section,
&self.date,
&self.source,
&self.manual,
]
}
/// Render the NAME section into the writer.
pub fn render_name_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
let mut roff = Roff::default();
@ -158,7 +175,7 @@ impl<'a> Man<'a> {
fn _render_subcommands_section(&self, roff: &mut Roff) {
let heading = subcommand_heading(&self.app);
roff.control("SH", [heading.as_str()]);
render::subcommands(roff, &self.app, &self.meta.section);
render::subcommands(roff, &self.app, &self.section);
}
/// Render the EXTRA section into the writer.

View file

@ -1,5 +1,5 @@
use clap::{arg, App};
use clap_man::generate_manpage;
use clap_man::Man;
use std::io;
#[test]
@ -25,5 +25,5 @@ fn render_manpage() {
.arg(arg!(-l --list "Lists test values")),
);
generate_manpage(app, &mut io::sink()).unwrap();
Man::new(app).render(&mut io::sink()).unwrap();
}