Skip to content

Commit 81bcade

Browse files
committed
refactor: functional style, some micro optimizations
1 parent 1d325a3 commit 81bcade

1 file changed

Lines changed: 48 additions & 51 deletions

File tree

src/man_page.rs

Lines changed: 48 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::args::Args;
22
use clap::{Arg, ArgAction, Command, CommandFactory};
3+
use itertools::Itertools;
34
use std::{borrow::Cow, collections::BTreeMap, fmt::Write};
45

56
/// A map from argument ID to the set of argument IDs it conflicts with (bidirectional).
@@ -73,21 +74,20 @@ fn render_name_section(out: &mut String, command: &Command) {
7374

7475
fn render_synopsis_section(out: &mut String, command: &Command) {
7576
out.push_str(".SH SYNOPSIS\n");
76-
out.push_str(&format!("\\fB{}\\fR", command.get_name()));
77-
for arg in command.get_arguments() {
78-
if arg.is_positional() {
79-
continue;
80-
}
81-
if arg.is_hide_set() {
82-
continue;
83-
}
77+
write!(out, "\\fB{}\\fR", command.get_name()).unwrap();
78+
let options = command
79+
.get_arguments()
80+
.filter(|arg| !arg.is_positional())
81+
.filter(|arg| !arg.is_hide_set());
82+
for arg in options {
8483
out.push(' ');
8584
render_synopsis_option(out, arg);
8685
}
87-
for arg in command.get_arguments() {
88-
if !arg.is_positional() || arg.is_hide_set() {
89-
continue;
90-
}
86+
let positionals = command
87+
.get_arguments()
88+
.filter(|arg| arg.is_positional())
89+
.filter(|arg| !arg.is_hide_set());
90+
for arg in positionals {
9191
out.push(' ');
9292
render_synopsis_positional(out, arg);
9393
}
@@ -202,17 +202,21 @@ fn render_option_header_positional(out: &mut String, arg: &Arg) {
202202
}
203203

204204
fn render_option_header_flag(out: &mut String, arg: &Arg) {
205-
let mut parts = Vec::new();
206-
if let Some(short) = arg.get_short() {
207-
parts.push(format!("\\fB\\-{}\\fR", roff_escape(&short.to_string())));
208-
}
209-
if let Some(long) = arg.get_long() {
210-
parts.push(format!("\\fB\\-\\-{}\\fR", roff_escape(long)));
211-
}
212-
for alias in arg.get_visible_aliases().unwrap_or_default() {
213-
parts.push(format!("\\fB\\-\\-{}\\fR", roff_escape(alias)));
214-
}
215-
let header = parts.join(", ");
205+
let short = arg
206+
.get_short()
207+
.map(|short| roff_escape(&short.to_string()))
208+
.map(|short| format!("\\fB\\-{short}\\fR"));
209+
let long = arg
210+
.get_long()
211+
.map(roff_escape)
212+
.map(|long| format!("\\fB\\-\\-{long}\\fR"));
213+
let aliases = arg
214+
.get_visible_aliases()
215+
.into_iter()
216+
.flatten()
217+
.map(roff_escape)
218+
.map(|arg| format!("\\fB\\-\\-{arg}\\fR"));
219+
let header = short.into_iter().chain(long).chain(aliases).join(", ");
216220
if arg.get_action().takes_values() {
217221
let value_str = render_value_hint(arg);
218222
writeln!(out, "{header} {value_str}").unwrap();
@@ -222,25 +226,26 @@ fn render_option_header_flag(out: &mut String, arg: &Arg) {
222226
}
223227

224228
fn render_value_hint(arg: &Arg) -> String {
225-
let mut parts = Vec::new();
226-
if let Some(value_names) = arg.get_value_names() {
227-
for name in value_names {
228-
parts.push(format!("\\fI<{}>\\fR", roff_escape(name)));
229-
}
230-
} else {
231-
parts.push(format!("\\fI<{}>\\fR", roff_escape(arg.get_id().as_str())));
232-
}
233-
let value_part = parts.join(" ");
229+
let value_part = arg
230+
.get_value_names()
231+
.map(<[_]>::iter)
232+
.map(|names| names.map(|name| name.as_str()))
233+
.map(|names| names.collect::<Vec<_>>())
234+
.unwrap_or_else(|| vec![arg.get_id().as_str()])
235+
.into_iter()
236+
.map(roff_escape)
237+
.map(|name| format!("\\fI<{name}>\\fR"))
238+
.join(" ");
234239
let defaults: Vec<_> = arg
235240
.get_default_values()
236241
.iter()
237242
.map(|value| value.to_string_lossy())
238243
.map(Cow::into_owned)
239244
.collect();
240-
if defaults.is_empty()
245+
let hide_defaults = defaults.is_empty()
241246
|| arg.is_hide_default_value_set()
242-
|| matches!(arg.get_action(), ArgAction::SetTrue)
243-
{
247+
|| matches!(arg.get_action(), ArgAction::SetTrue);
248+
if hide_defaults {
244249
value_part
245250
} else {
246251
format!("{value_part} [default: {}]", defaults.join(", "))
@@ -289,23 +294,15 @@ fn render_possible_values(out: &mut String, arg: &Arg) {
289294

290295
fn render_conflicts(out: &mut String, command: &Command, arg: &Arg, conflict_map: &ConflictMap) {
291296
let arg_id = arg.get_id().as_str();
292-
let conflict_ids = match conflict_map.get(arg_id) {
293-
Some(ids) if !ids.is_empty() => ids,
294-
_ => return,
295-
};
296-
let conflict_names: Vec<_> = conflict_ids
297-
.iter()
298-
.filter_map(|conflict_id| resolve_flag_name(command, conflict_id))
299-
.collect();
300-
if conflict_names.is_empty() {
301-
return;
297+
let conflicts = conflict_map
298+
.get(arg_id)
299+
.into_iter()
300+
.flatten()
301+
.filter_map(|id| resolve_flag_name(command, id))
302+
.join(", ");
303+
if !conflicts.is_empty() {
304+
writeln!(out, ".RS\n.PP\nCannot be used with {conflicts}.\n.RE").unwrap();
302305
}
303-
writeln!(
304-
out,
305-
".RS\n.PP\nCannot be used with {}.\n.RE",
306-
conflict_names.join(", ")
307-
)
308-
.unwrap();
309306
}
310307

311308
fn render_examples_section(out: &mut String, command: &Command) {

0 commit comments

Comments
 (0)