Skip to content

Commit 2d745ae

Browse files
CopilotKSXGitHub
andcommitted
refactor(usage_md): extract helper functions, fix var names, use current_title pattern
Co-authored-by: KSXGitHub <11488886+KSXGitHub@users.noreply.github.com>
1 parent c642f25 commit 2d745ae

1 file changed

Lines changed: 86 additions & 67 deletions

File tree

src/usage_md.rs

Lines changed: 86 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::args::Args;
2+
use clap::builder::PossibleValue;
23
use clap::{Arg, Command, CommandFactory};
34
use itertools::Itertools;
45
use pipe_trait::Pipe;
@@ -86,63 +87,12 @@ fn render_option(arg: &Arg, out: &mut String) {
8687
let primary_long = arg.get_long().expect("option must have a long flag");
8788
let primary_name = format!("--{primary_long}");
8889

89-
let short = arg.get_short();
90-
let visible_long_aliases: Vec<&str> = arg.get_visible_aliases().unwrap_or_default();
91-
let visible_short_aliases: Vec<char> = arg.get_visible_short_aliases().unwrap_or_default();
92-
93-
// Invisible anchors: short first, then primary long, then long aliases
94-
let mut anchor_ids: Vec<String> = Vec::new();
95-
if let Some(short) = short {
96-
anchor_ids.push(format!("option-{short}"));
97-
}
98-
anchor_ids.push(primary_long.to_string());
99-
for &alias in &visible_long_aliases {
100-
anchor_ids.push(alias.to_string());
101-
}
102-
for char in &visible_short_aliases {
103-
anchor_ids.push(format!("option-{char}"));
104-
}
105-
for id in &anchor_ids {
106-
out.push_str(&format!(r#"<a id="{id}" name="{id}"></a>"#));
107-
}
108-
out.push('\n');
109-
110-
// Heading
90+
write_option_anchors(arg, primary_long, out);
11191
out.push_str(&format!("### `{primary_name}`\n\n"));
11292

113-
// Aliases for display in metadata
114-
let mut aliases: Vec<String> = Vec::new();
115-
if let Some(short) = short {
116-
aliases.push(format!("-{short}"));
117-
}
118-
for &alias in &visible_long_aliases {
119-
aliases.push(format!("--{alias}"));
120-
}
121-
for alias in &visible_short_aliases {
122-
aliases.push(format!("-{alias}"));
123-
}
124-
125-
// Default values – skip "false" (clap's implicit default for boolean flags)
126-
let default_values: Vec<String> = if arg.is_hide_default_value_set() {
127-
Vec::new()
128-
} else {
129-
arg.get_default_values()
130-
.iter()
131-
.map(|value| value.to_string_lossy())
132-
.filter(|value| value != "false")
133-
.map(|value| value.to_string())
134-
.collect()
135-
};
136-
137-
// Possible values (choices)
138-
let possible_values: Vec<_> = if arg.is_hide_possible_values_set() {
139-
Vec::new()
140-
} else {
141-
arg.get_possible_values()
142-
.into_iter()
143-
.filter(|value| !value.is_hide_set())
144-
.collect()
145-
};
93+
let aliases = collect_option_display_aliases(arg);
94+
let default_values = collect_option_default_values(arg);
95+
let possible_values = collect_option_possible_values(arg);
14696

14797
let has_metadata =
14898
!aliases.is_empty() || !default_values.is_empty() || !possible_values.is_empty();
@@ -152,14 +102,14 @@ fn render_option(arg: &Arg, out: &mut String) {
152102
out.push_str(&format!("* _Aliases:_ {aliases_str}.\n"));
153103
}
154104
if !default_values.is_empty() {
155-
let default_values = default_values.join(", ");
156-
out.push_str(&format!("* _Default:_ `{default_values}`.\n"));
105+
let default_values_str = default_values.join(", ");
106+
out.push_str(&format!("* _Default:_ `{default_values_str}`.\n"));
157107
}
158108
if !possible_values.is_empty() {
159109
out.push_str("* _Choices:_\n");
160-
for value in &possible_values {
161-
let name = value.get_name();
162-
if let Some(help) = value.get_help() {
110+
for possible_value in &possible_values {
111+
let name = possible_value.get_name();
112+
if let Some(help) = possible_value.get_help() {
163113
out.push_str(&format!(" - `{name}`: {help}\n"));
164114
} else {
165115
out.push_str(&format!(" - `{name}`\n"));
@@ -171,7 +121,72 @@ fn render_option(arg: &Arg, out: &mut String) {
171121
out.push('\n');
172122
}
173123

174-
// Description: short help, with long help appended if also set
124+
write_option_description(arg, out);
125+
}
126+
127+
fn write_option_anchors(arg: &Arg, primary_long: &str, out: &mut String) {
128+
let short = arg.get_short();
129+
let visible_long_aliases: Vec<&str> = arg.get_visible_aliases().unwrap_or_default();
130+
let visible_short_aliases: Vec<char> = arg.get_visible_short_aliases().unwrap_or_default();
131+
132+
let mut anchor_ids: Vec<String> = Vec::new();
133+
if let Some(short_char) = short {
134+
anchor_ids.push(format!("option-{short_char}"));
135+
}
136+
anchor_ids.push(primary_long.to_string());
137+
for &long_alias in &visible_long_aliases {
138+
anchor_ids.push(long_alias.to_string());
139+
}
140+
for &short_alias in &visible_short_aliases {
141+
anchor_ids.push(format!("option-{short_alias}"));
142+
}
143+
for id in &anchor_ids {
144+
out.push_str(&format!(r#"<a id="{id}" name="{id}"></a>"#));
145+
}
146+
out.push('\n');
147+
}
148+
149+
fn collect_option_display_aliases(arg: &Arg) -> Vec<String> {
150+
let short = arg.get_short();
151+
let visible_long_aliases: Vec<&str> = arg.get_visible_aliases().unwrap_or_default();
152+
let visible_short_aliases: Vec<char> = arg.get_visible_short_aliases().unwrap_or_default();
153+
154+
let mut aliases: Vec<String> = Vec::new();
155+
if let Some(short_char) = short {
156+
aliases.push(format!("-{short_char}"));
157+
}
158+
for &long_alias in &visible_long_aliases {
159+
aliases.push(format!("--{long_alias}"));
160+
}
161+
for &short_alias in &visible_short_aliases {
162+
aliases.push(format!("-{short_alias}"));
163+
}
164+
aliases
165+
}
166+
167+
fn collect_option_default_values(arg: &Arg) -> Vec<String> {
168+
if arg.is_hide_default_value_set() {
169+
return Vec::new();
170+
}
171+
arg.get_default_values()
172+
.iter()
173+
.map(|value| value.to_string_lossy())
174+
.filter(|value| value != "false")
175+
.map(|value| value.to_string())
176+
.collect()
177+
}
178+
179+
fn collect_option_possible_values(arg: &Arg) -> Vec<PossibleValue> {
180+
if arg.is_hide_possible_values_set() {
181+
return Vec::new();
182+
}
183+
arg.get_possible_values()
184+
.into_iter()
185+
.filter(|possible_value| !possible_value.is_hide_set())
186+
.collect()
187+
}
188+
189+
fn write_option_description(arg: &Arg, out: &mut String) {
175190
let description = get_help_text(arg);
176191
if !description.is_empty() {
177192
let description = ensure_ends_with_punctuation(&description);
@@ -190,20 +205,24 @@ fn get_help_text(arg: &Arg) -> Cow<'static, str> {
190205
}
191206

192207
fn render_examples_section<'a>(lines: impl Iterator<Item = &'a str>, out: &mut String) {
208+
let mut current_title: Option<&'a str> = None;
193209
for line in lines {
194210
let line = line.trim();
195211

196212
if line.is_empty() {
197213
continue;
198214
}
199215

200-
if let Some(command) = line.strip_prefix('$') {
201-
let command = command.trim();
202-
out.push_str(&format!("```sh\n{command}\n```\n\n"));
203-
continue;
216+
if let Some(cmd) = line.strip_prefix('$').map(str::trim) {
217+
let heading = if let Some(title) = current_title.take() {
218+
title.to_string()
219+
} else {
220+
format!("`{cmd}`")
221+
};
222+
out.push_str(&format!("### {heading}\n\n```sh\n{cmd}\n```\n\n"));
223+
} else {
224+
current_title = Some(line);
204225
}
205-
206-
out.push_str(&format!("### {line}\n\n"));
207226
}
208227
}
209228

0 commit comments

Comments
 (0)