Repository: clap-rs/clap
Pull request #3364 (2022-01-28 21:53:48)

feat(man)!: Allow customizing man page
This is part of #3354

BREAKING CHANGE: Functions now take ownership


Patch:
diff --git a/clap_man/examples/man-builder.rs b/clap_man/examples/man-builder.rs
index e93eeabc..a9466463 100644
--- a/clap_man/examples/man-builder.rs
+++ b/clap_man/examples/man-builder.rs
@@ -4,7 +4,7 @@ use clap_man::{Man, Meta};
 // Run this example as `cargo run --example man-builder | man -l -`.
 
 fn main() -> Result<(), std::io::Error> {
-    let mut app = App::new("myapp")
+    let app = App::new("myapp")
         .version("1.0")
         .author("Kevin K. <kbknapp@gmail.com>")
         .about("Does awesome things")
@@ -29,6 +29,6 @@ To see the debug information, visit our website on github.com",
         );
 
     let meta = Meta::from_clap("1", "GNU", &app);
-    let man = Man::new(&meta, &mut app);
+    let man = Man::new(meta, app);
     man.render(&mut std::io::stdout())
 }
diff --git a/clap_man/examples/man.rs b/clap_man/examples/man.rs
index 3b0f3b63..779f6455 100644
--- a/clap_man/examples/man.rs
+++ b/clap_man/examples/man.rs
@@ -5,7 +5,7 @@ use std::io;
 // Run this example as `cargo run --example man | man -l -`.
 
 fn main() -> Result<(), std::io::Error> {
-    let mut app = App::new("myapp")
+    let app = App::new("myapp")
         .version("1.0")
         .author("Kevin K. <kbknapp@gmail.com>:Ola Nordmann <old@nordmann.no>")
         .about("Does awesome things")
@@ -36,5 +36,5 @@ And a few newlines.",
                 .arg(arg!(-l --list "Lists test values")),
         );
 
-    generate_manpage(&mut app, &mut io::stdout())
+    generate_manpage(app, &mut io::stdout())
 }
diff --git a/clap_man/src/lib.rs b/clap_man/src/lib.rs
index cda15695..158bfd36 100644
--- a/clap_man/src/lib.rs
+++ b/clap_man/src/lib.rs
@@ -13,12 +13,9 @@ 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);
+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)
 }
 
@@ -60,58 +57,146 @@ impl Meta {
 }
 
 /// A manual page as constructed from a clap::App.
-pub struct Man {
-    roff: Roff,
+pub struct Man<'a> {
+    meta: Meta,
+    app: clap::App<'a>,
 }
 
-impl Man {
+impl<'a> Man<'a> {
     /// Create a new manual page.
-    pub fn new(meta: &Meta, app: &mut clap::App) -> Self {
+    pub fn new(meta: Meta, mut app: clap::App<'a>) -> Self {
         app._build_all();
+        Self { meta, app }
+    }
 
+    /// 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", 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);
+        roff.control("TH", self.meta.to_args());
+        self._render_name_section(&mut roff);
+        self._render_synopsis_section(&mut roff);
+        self._render_description_section(&mut roff);
 
-        if app_has_arguments(app) {
-            roff.control("SH", ["OPTIONS"]);
-            render::options(&mut roff, app);
+        if app_has_arguments(&self.app) {
+            self._render_options_section(&mut roff);
         }
 
-        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_has_subcommands(&self.app) {
+            self._render_subcommands_section(&mut roff);
         }
 
-        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 self.app.get_after_long_help().is_some() || self.app.get_after_help().is_some() {
+            self._render_extra_section(&mut roff);
         }
 
-        if app_has_version(app) {
-            let version = roman(&render::version(app));
-            roff.control("SH", ["VERSION"]);
-            roff.text([version]);
+        if app_has_version(&self.app) {
+            self._render_version_section(&mut roff);
         }
 
-        if app.get_author().is_some() {
-            let author = roman(app.get_author().unwrap_or_default());
-            roff.control("SH", ["AUTHORS"]);
-            roff.text([author]);
+        if self.app.get_author().is_some() {
+            self._render_authors_section(&mut roff);
         }
 
-        Self { roff }
+        roff.to_writer(w)
     }
 
-    /// Render a manual page into writer.
-    pub fn render(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
-        self.roff.to_writer(w)
+    /// 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();
+        self._render_name_section(&mut roff);
+        roff.to_writer(w)
+    }
+
+    fn _render_name_section(&self, roff: &mut Roff) {
+        roff.control("SH", ["NAME"]);
+        render::about(roff, &self.app);
+    }
+
+    /// Render the SYNOPSIS section into the writer.
+    pub fn render_synopsis_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
+        let mut roff = Roff::default();
+        self._render_synopsis_section(&mut roff);
+        roff.to_writer(w)
+    }
+
+    fn _render_synopsis_section(&self, roff: &mut Roff) {
+        roff.control("SH", ["SYNOPSIS"]);
+        render::synopsis(roff, &self.app);
+    }
+
+    /// Render the DESCRIPTION section into the writer.
+    pub fn render_description_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
+        let mut roff = Roff::default();
+        self._render_description_section(&mut roff);
+        roff.to_writer(w)
+    }
+
+    fn _render_description_section(&self, roff: &mut Roff) {
+        roff.control("SH", ["DESCRIPTION"]);
+        render::description(roff, &self.app);
+    }
+
+    /// Render the OPTIONS section into the writer.
+    pub fn render_options_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
+        let mut roff = Roff::default();
+        self._render_options_section(&mut roff);
+        roff.to_writer(w)
+    }
+
+    fn _render_options_section(&self, roff: &mut Roff) {
+        roff.control("SH", ["OPTIONS"]);
+        render::options(roff, &self.app);
+    }
+
+    /// Render the SUBCOMMANDS section into the writer.
+    pub fn render_subcommands_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
+        let mut roff = Roff::default();
+        self._render_subcommands_section(&mut roff);
+        roff.to_writer(w)
+    }
+
+    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 the EXTRA section into the writer.
+    pub fn render_extra_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
+        let mut roff = Roff::default();
+        self._render_extra_section(&mut roff);
+        roff.to_writer(w)
+    }
+
+    fn _render_extra_section(&self, roff: &mut Roff) {
+        roff.control("SH", ["EXTRA"]);
+        render::after_help(roff, &self.app);
+    }
+
+    /// Render the VERSION section into the writer.
+    pub fn render_version_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
+        let mut roff = Roff::default();
+        self._render_version_section(&mut roff);
+        roff.to_writer(w)
+    }
+
+    fn _render_version_section(&self, roff: &mut Roff) {
+        let version = roman(&render::version(&self.app));
+        roff.control("SH", ["VERSION"]);
+        roff.text([version]);
+    }
+
+    /// Render the AUTHORS section into the writer.
+    pub fn render_authors_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
+        let mut roff = Roff::default();
+        self._render_authors_section(&mut roff);
+        roff.to_writer(w)
+    }
+
+    fn _render_authors_section(&self, roff: &mut Roff) {
+        let author = roman(self.app.get_author().unwrap_or_default());
+        roff.control("SH", ["AUTHORS"]);
+        roff.text([author]);
     }
 }
 


Test patch:
diff --git a/clap_man/tests/generate.rs b/clap_man/tests/generate.rs
index 422f7d3f..4733873d 100644
--- a/clap_man/tests/generate.rs
+++ b/clap_man/tests/generate.rs
@@ -4,7 +4,7 @@ use std::io;
 
 #[test]
 fn render_manpage() {
-    let mut app = App::new("myapp")
+    let app = App::new("myapp")
         .version("1.0")
         .author("Kevin K. <kbknapp@gmail.com>")
         .about("Does awesome things")
@@ -25,5 +25,5 @@ fn render_manpage() {
                 .arg(arg!(-l --list "Lists test values")),
         );
 
-    generate_manpage(&mut app, &mut io::sink()).unwrap();
+    generate_manpage(app, &mut io::sink()).unwrap();
 }