|
1 | 1 | use std::{fs, path::PathBuf, process}; |
2 | 2 |
|
3 | | -use clap::{CommandFactory, Parser, builder::Resettable}; |
| 3 | +use clap::{CommandFactory, Parser}; |
4 | 4 |
|
5 | 5 | fn main() { |
6 | 6 | if let Err(e) = run() { |
@@ -64,169 +64,19 @@ fn mangen(args: MangenArgs) -> Result<(), Box<dyn std::error::Error>> { |
64 | 64 | fn docgen(args: DocgenArgs) -> Result<(), Box<dyn std::error::Error>> { |
65 | 65 | let out_path = &args.output; |
66 | 66 |
|
67 | | - // Create parent directory if it doesn't exist |
68 | | - if let Some(parent) = out_path.parent() { |
69 | | - fs::create_dir_all(parent)?; |
70 | | - } |
71 | | - |
72 | 67 | // Get the CLI command and rename to match the binary name |
73 | | - let mut cmd = portable_network_archive::cli::Cli::command().name("pna"); |
74 | | - |
75 | | - // Build the command to propagate global arguments to subcommands. |
76 | | - // This is necessary because clap-markdown iterates through subcommands |
77 | | - // and global argument references need to be resolved first. |
78 | | - cmd.build(); |
79 | | - |
80 | | - // After build(), clap sets display_name and bin_name on subcommands to include |
81 | | - // the parent name (e.g., display_name="pna-create", bin_name="pna create"). |
82 | | - // This causes two problems in clap-markdown: |
83 | | - // 1. Section headers show "pna pna-create" instead of "pna create" |
84 | | - // (because display_name is used in path building) |
85 | | - // 2. Usage lines show "pna pna create" instead of "pna create" |
86 | | - // (because render_usage() uses bin_name, then clap-markdown prepends parent path) |
87 | | - // |
88 | | - // We need to clear display_name on all subcommands so clap-markdown |
89 | | - // uses the command name instead of display_name for section headers. |
90 | | - clear_display_names(&mut cmd); |
| 68 | + let cmd = portable_network_archive::cli::Cli::command().name("pna"); |
91 | 69 |
|
92 | 70 | // Generate markdown documentation |
93 | 71 | let markdown = clap_markdown::help_markdown_command(&cmd); |
94 | 72 |
|
95 | | - // Post-process to fix duplicate command paths. |
96 | | - // After build(), clap's render_usage() includes the full path (e.g., "pna create"), |
97 | | - // but clap-markdown also prepends the parent path, resulting in duplicates like: |
98 | | - // - "pna pna create" (for top-level subcommands) |
99 | | - // - "pna xattr pna xattr get" (for nested subcommands) |
100 | | - // - "pna-pna create" (in TOC links/anchors) |
101 | | - let markdown = fix_duplicate_command_paths(&markdown); |
| 73 | + // Create a parent directory if it doesn't exist |
| 74 | + if let Some(parent) = out_path.parent() { |
| 75 | + fs::create_dir_all(parent)?; |
| 76 | + } |
102 | 77 |
|
103 | 78 | fs::write(out_path, &markdown)?; |
104 | 79 |
|
105 | 80 | eprintln!("Markdown documentation generated: {}", out_path.display()); |
106 | 81 | Ok(()) |
107 | 82 | } |
108 | | - |
109 | | -/// Recursively clear display_name from all subcommands. |
110 | | -/// |
111 | | -/// After `Command::build()` is called, clap automatically sets display_name |
112 | | -/// on subcommands (e.g., "pna-create"). This causes clap-markdown to generate |
113 | | -/// incorrect section headers like "pna pna-create" instead of "pna create". |
114 | | -fn clear_display_names(cmd: &mut clap::Command) { |
115 | | - for sub in cmd.get_subcommands_mut() { |
116 | | - let mut owned = std::mem::take(sub); |
117 | | - owned = owned.display_name(Resettable::Reset); |
118 | | - clear_display_names(&mut owned); |
119 | | - *sub = owned; |
120 | | - } |
121 | | -} |
122 | | - |
123 | | -/// Fix duplicate command paths in the generated markdown. |
124 | | -/// |
125 | | -/// clap-markdown has a bug where it prepends the parent command path to the |
126 | | -/// usage string, but clap's render_usage() already includes the full path. |
127 | | -/// This results in duplicates like "pna xattr pna xattr get". |
128 | | -/// |
129 | | -/// This function removes these duplicated path segments using simple string |
130 | | -/// replacement, iterating until no more changes are made. |
131 | | -fn fix_duplicate_command_paths(markdown: &str) -> String { |
132 | | - let mut result = markdown.to_string(); |
133 | | - |
134 | | - // Keep replacing until no more changes |
135 | | - loop { |
136 | | - let prev = result.clone(); |
137 | | - |
138 | | - // Fix "pna pna " -> "pna " (simple duplicate) |
139 | | - result = result.replace("pna pna ", "pna "); |
140 | | - |
141 | | - // Fix "pna X pna X " patterns where X is a subcommand |
142 | | - // We enumerate known subcommand prefixes that might be duplicated |
143 | | - let subcommands = [ |
144 | | - "create", |
145 | | - "append", |
146 | | - "extract", |
147 | | - "list", |
148 | | - "split", |
149 | | - "concat", |
150 | | - "strip", |
151 | | - "xattr", |
152 | | - "complete", |
153 | | - "bug-report", |
154 | | - "experimental", |
155 | | - "help", |
156 | | - "stdio", |
157 | | - "delete", |
158 | | - "update", |
159 | | - "chown", |
160 | | - "chmod", |
161 | | - "acl", |
162 | | - "migrate", |
163 | | - "chunk", |
164 | | - "sort", |
165 | | - "diff", |
166 | | - "get", |
167 | | - "set", |
168 | | - ]; |
169 | | - |
170 | | - for sub in &subcommands { |
171 | | - // Fix patterns like "pna xattr pna xattr " -> "pna xattr " |
172 | | - let dup_pattern = format!("pna {sub} pna {sub} "); |
173 | | - let replacement = format!("pna {sub} "); |
174 | | - result = result.replace(&dup_pattern, &replacement); |
175 | | - |
176 | | - // Also handle end-of-command patterns (no trailing space) |
177 | | - let dup_pattern_end = format!("pna {sub} pna {sub}`"); |
178 | | - let replacement_end = format!("pna {sub}`"); |
179 | | - result = result.replace(&dup_pattern_end, &replacement_end); |
180 | | - } |
181 | | - |
182 | | - // Fix nested patterns like "pna experimental stdio pna experimental stdio" |
183 | | - for sub1 in &subcommands { |
184 | | - for sub2 in &subcommands { |
185 | | - let dup = format!("pna {sub1} {sub2} pna {sub1} {sub2}"); |
186 | | - let replacement = format!("pna {sub1} {sub2}"); |
187 | | - result = result.replace(&dup, &replacement); |
188 | | - } |
189 | | - } |
190 | | - |
191 | | - // Fix 3-level nested patterns |
192 | | - for sub1 in &["experimental", "help", "xattr", "acl", "chunk"] { |
193 | | - for sub2 in &subcommands { |
194 | | - for sub3 in &subcommands { |
195 | | - let dup = format!("pna {sub1} {sub2} {sub3} pna {sub1} {sub2} {sub3}"); |
196 | | - let replacement = format!("pna {sub1} {sub2} {sub3}"); |
197 | | - result = result.replace(&dup, &replacement); |
198 | | - } |
199 | | - } |
200 | | - } |
201 | | - |
202 | | - // Fix hyphenated anchors: "#pna-pna-" -> "#pna-" |
203 | | - result = result.replace("#pna-pna-", "#pna-"); |
204 | | - |
205 | | - // Fix hyphenated duplicates in anchors |
206 | | - for sub in &subcommands { |
207 | | - // Fix "#pna-X-pna-X" -> "#pna-X" |
208 | | - let dup = format!("#pna-{sub}-pna-{sub}"); |
209 | | - let replacement = format!("#pna-{sub}"); |
210 | | - result = result.replace(&dup, &replacement); |
211 | | - } |
212 | | - |
213 | | - // Fix 2-level hyphenated patterns |
214 | | - for sub1 in &subcommands { |
215 | | - for sub2 in &subcommands { |
216 | | - let dup = format!("-pna-{sub1}-{sub2}-pna-{sub1}-{sub2}"); |
217 | | - let replacement = format!("-pna-{sub1}-{sub2}"); |
218 | | - result = result.replace(&dup, &replacement); |
219 | | - |
220 | | - let dup2 = format!("#pna-{sub1}-pna-{sub1}-{sub2}"); |
221 | | - let replacement2 = format!("#pna-{sub1}-{sub2}"); |
222 | | - result = result.replace(&dup2, &replacement2); |
223 | | - } |
224 | | - } |
225 | | - |
226 | | - if result == prev { |
227 | | - break; |
228 | | - } |
229 | | - } |
230 | | - |
231 | | - result |
232 | | -} |
0 commit comments