• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #![doc(html_logo_url = "https://raw.githubusercontent.com/clap-rs/clap/master/assets/clap.png")]
2 #![doc = include_str!("../README.md")]
3 #![warn(missing_docs, trivial_casts, unused_allocation, trivial_numeric_casts)]
4 #![forbid(unsafe_code)]
5 #![deny(missing_docs)]
6 
7 mod render;
8 
9 pub use roff;
10 
11 use render::subcommand_heading;
12 use roff::{roman, Roff};
13 use std::io::Write;
14 
15 /// A manpage writer
16 pub struct Man {
17     cmd: clap::Command,
18     title: String,
19     section: String,
20     date: String,
21     source: String,
22     manual: String,
23 }
24 
25 /// Build a [`Man`]
26 impl Man {
27     /// Create a new manual page.
new(mut cmd: clap::Command) -> Self28     pub fn new(mut cmd: clap::Command) -> Self {
29         cmd.build();
30         let title = cmd.get_name().to_owned();
31         let section = "1".to_owned();
32         let date = "".to_owned();
33         let source = format!(
34             "{} {}",
35             cmd.get_name(),
36             cmd.get_version().unwrap_or_default()
37         );
38         let manual = "".to_owned();
39         Self {
40             cmd,
41             title,
42             section,
43             date,
44             source,
45             manual,
46         }
47     }
48 
49     /// Override the default man page title, written in all caps
title(mut self, title: impl Into<String>) -> Self50     pub fn title(mut self, title: impl Into<String>) -> Self {
51         self.title = title.into();
52         self
53     }
54 
55     /// Override the default section this man page is placed in
56     ///
57     /// Common values:
58     ///
59     /// - `"1"`: User Commands
60     /// - `"2"`: System Calls
61     /// - `"3"`: C Library Functions
62     /// - `"4"`: Devices and Special Files
63     /// - `"5"`: File Formats and Conventions
64     /// - `"6"`: Games et. al.
65     /// - `"7"`: Miscellanea
66     /// - `"8"`: System Administration tools and Daemons
section(mut self, section: impl Into<String>) -> Self67     pub fn section(mut self, section: impl Into<String>) -> Self {
68         self.section = section.into();
69         self
70     }
71 
72     /// Override the default date for the last non-trivial change to this man page
73     ///
74     /// Dates should be written in the form `YYYY-MM-DD`.
date(mut self, date: impl Into<String>) -> Self75     pub fn date(mut self, date: impl Into<String>) -> Self {
76         self.date = date.into();
77         self
78     }
79 
80     /// Override the default source your command
81     ///
82     /// For those few man-pages pages in Sections 1 and 8, probably you just want to write GNU.
source(mut self, source: impl Into<String>) -> Self83     pub fn source(mut self, source: impl Into<String>) -> Self {
84         self.source = source.into();
85         self
86     }
87 
88     /// Override the default manual this page is a member of
manual(mut self, manual: impl Into<String>) -> Self89     pub fn manual(mut self, manual: impl Into<String>) -> Self {
90         self.manual = manual.into();
91         self
92     }
93 }
94 
95 /// Generate ROFF output
96 impl Man {
97     /// Render a full manual page into the writer.
98     ///
99     /// If customization is needed, you can call the individual sections you want and mix them into
100     /// your own ROFF content.
render(&self, w: &mut dyn Write) -> Result<(), std::io::Error>101     pub fn render(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
102         let mut roff = Roff::default();
103         self._render_title(&mut roff);
104         self._render_name_section(&mut roff);
105         self._render_synopsis_section(&mut roff);
106         self._render_description_section(&mut roff);
107 
108         if app_has_arguments(&self.cmd) {
109             self._render_options_section(&mut roff);
110         }
111 
112         if app_has_subcommands(&self.cmd) {
113             self._render_subcommands_section(&mut roff);
114         }
115 
116         if self.cmd.get_after_long_help().is_some() || self.cmd.get_after_help().is_some() {
117             self._render_extra_section(&mut roff);
118         }
119 
120         if app_has_version(&self.cmd) {
121             self._render_version_section(&mut roff);
122         }
123 
124         if self.cmd.get_author().is_some() {
125             self._render_authors_section(&mut roff);
126         }
127 
128         roff.to_writer(w)
129     }
130 
131     /// Render the title into the writer.
render_title(&self, w: &mut dyn Write) -> Result<(), std::io::Error>132     pub fn render_title(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
133         let mut roff = Roff::default();
134         self._render_title(&mut roff);
135         roff.to_writer(w)
136     }
137 
_render_title(&self, roff: &mut Roff)138     fn _render_title(&self, roff: &mut Roff) {
139         roff.control("TH", self.title_args());
140     }
141 
142     // Turn metadata into arguments for a .TH macro.
title_args(&self) -> Vec<&str>143     fn title_args(&self) -> Vec<&str> {
144         vec![
145             &self.title,
146             &self.section,
147             &self.date,
148             &self.source,
149             &self.manual,
150         ]
151     }
152 
153     /// Render the NAME section into the writer.
render_name_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error>154     pub fn render_name_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
155         let mut roff = Roff::default();
156         self._render_name_section(&mut roff);
157         roff.to_writer(w)
158     }
159 
_render_name_section(&self, roff: &mut Roff)160     fn _render_name_section(&self, roff: &mut Roff) {
161         roff.control("SH", ["NAME"]);
162         render::about(roff, &self.cmd);
163     }
164 
165     /// Render the SYNOPSIS section into the writer.
render_synopsis_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error>166     pub fn render_synopsis_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
167         let mut roff = Roff::default();
168         self._render_synopsis_section(&mut roff);
169         roff.to_writer(w)
170     }
171 
_render_synopsis_section(&self, roff: &mut Roff)172     fn _render_synopsis_section(&self, roff: &mut Roff) {
173         roff.control("SH", ["SYNOPSIS"]);
174         render::synopsis(roff, &self.cmd);
175     }
176 
177     /// Render the DESCRIPTION section into the writer.
render_description_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error>178     pub fn render_description_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
179         let mut roff = Roff::default();
180         self._render_description_section(&mut roff);
181         roff.to_writer(w)
182     }
183 
_render_description_section(&self, roff: &mut Roff)184     fn _render_description_section(&self, roff: &mut Roff) {
185         roff.control("SH", ["DESCRIPTION"]);
186         render::description(roff, &self.cmd);
187     }
188 
189     /// Render the OPTIONS section into the writer.
render_options_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error>190     pub fn render_options_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
191         let mut roff = Roff::default();
192         self._render_options_section(&mut roff);
193         roff.to_writer(w)
194     }
195 
_render_options_section(&self, roff: &mut Roff)196     fn _render_options_section(&self, roff: &mut Roff) {
197         roff.control("SH", ["OPTIONS"]);
198         render::options(roff, &self.cmd);
199     }
200 
201     /// Render the SUBCOMMANDS section into the writer.
render_subcommands_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error>202     pub fn render_subcommands_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
203         let mut roff = Roff::default();
204         self._render_subcommands_section(&mut roff);
205         roff.to_writer(w)
206     }
207 
_render_subcommands_section(&self, roff: &mut Roff)208     fn _render_subcommands_section(&self, roff: &mut Roff) {
209         let heading = subcommand_heading(&self.cmd);
210         roff.control("SH", [heading]);
211         render::subcommands(roff, &self.cmd, &self.section);
212     }
213 
214     /// Render the EXTRA section into the writer.
render_extra_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error>215     pub fn render_extra_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
216         let mut roff = Roff::default();
217         self._render_extra_section(&mut roff);
218         roff.to_writer(w)
219     }
220 
_render_extra_section(&self, roff: &mut Roff)221     fn _render_extra_section(&self, roff: &mut Roff) {
222         roff.control("SH", ["EXTRA"]);
223         render::after_help(roff, &self.cmd);
224     }
225 
226     /// Render the VERSION section into the writer.
render_version_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error>227     pub fn render_version_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
228         let mut roff = Roff::default();
229         self._render_version_section(&mut roff);
230         roff.to_writer(w)
231     }
232 
_render_version_section(&self, roff: &mut Roff)233     fn _render_version_section(&self, roff: &mut Roff) {
234         let version = roman(render::version(&self.cmd));
235         roff.control("SH", ["VERSION"]);
236         roff.text([version]);
237     }
238 
239     /// Render the AUTHORS section into the writer.
render_authors_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error>240     pub fn render_authors_section(&self, w: &mut dyn Write) -> Result<(), std::io::Error> {
241         let mut roff = Roff::default();
242         self._render_authors_section(&mut roff);
243         roff.to_writer(w)
244     }
245 
_render_authors_section(&self, roff: &mut Roff)246     fn _render_authors_section(&self, roff: &mut Roff) {
247         let author = roman(self.cmd.get_author().unwrap_or_default());
248         roff.control("SH", ["AUTHORS"]);
249         roff.text([author]);
250     }
251 }
252 
253 // Does the application have a version?
app_has_version(cmd: &clap::Command) -> bool254 fn app_has_version(cmd: &clap::Command) -> bool {
255     cmd.get_version()
256         .or_else(|| cmd.get_long_version())
257         .is_some()
258 }
259 
260 // Does the application have any command line arguments?
app_has_arguments(cmd: &clap::Command) -> bool261 fn app_has_arguments(cmd: &clap::Command) -> bool {
262     cmd.get_arguments().any(|i| !i.is_hide_set())
263 }
264 
265 // Does the application have any subcommands?
app_has_subcommands(cmd: &clap::Command) -> bool266 fn app_has_subcommands(cmd: &clap::Command) -> bool {
267     cmd.get_subcommands().any(|i| !i.is_hide_set())
268 }
269