Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 477b37a

Browse files
Add example to manpage (#7841)
1 parent b2d0773 commit 477b37a

File tree

4 files changed

+124
-67
lines changed

4 files changed

+124
-67
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ expensive_tests = []
3131
# "test_risky_names" == enable tests that create problematic file names (would make a network share inaccessible to Windows, breaks SVN on Mac OS, etc.)
3232
test_risky_names = []
3333
# * only build `uudoc` when `--feature uudoc` is activated
34-
uudoc = ["clap_complete", "clap_mangen", "fluent-syntax", "zip"]
34+
uudoc = ["dep:clap_complete", "dep:clap_mangen", "dep:fluent-syntax", "dep:zip"]
3535
## features
3636
## Optional feature for stdbuf
3737
# "feat_external_libstdbuf" == use an external libstdbuf.so for stdbuf instead of embedding it

src/bin/coreutils.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,28 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55

6+
use clap::Command;
7+
use coreutils::validation;
68
use std::cmp;
79
use std::ffi::OsString;
810
use std::io::{self, Write};
911
use std::process;
1012

11-
use clap::Command;
12-
13-
use coreutils::validation;
14-
1513
const VERSION: &str = env!("CARGO_PKG_VERSION");
1614

1715
include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"));
1816

1917
fn usage<T>(utils: &UtilityMap<T>, name: &str) {
2018
println!("{name} {VERSION} (multi-call binary)\n");
2119
println!("Usage: {name} [function [arguments...]]");
22-
println!(" {name} --list\n");
20+
println!(" {name} --list");
21+
println!();
22+
#[cfg(feature = "feat_common_core")]
23+
{
24+
println!("Functions:");
25+
println!(" '<uutils>' [arguments...]");
26+
println!();
27+
}
2328
println!("Options:");
2429
println!(" --list lists all defined functions, one per row\n");
2530
println!("Currently defined functions:\n");

src/bin/uudoc.rs

Lines changed: 107 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@ fn gen_coreutils_app<T: Args>(util_map: &UtilityMap<T>) -> clap::Command {
6161
}
6262

6363
/// Generate the manpage for the utility in the first parameter
64-
fn gen_manpage<T: Args>(args: impl Iterator<Item = OsString>, util_map: &UtilityMap<T>) -> ! {
64+
fn gen_manpage<T: Args>(
65+
tldr: &mut Option<ZipArchive<File>>,
66+
args: impl Iterator<Item = OsString>,
67+
util_map: &UtilityMap<T>,
68+
) -> ! {
6569
let all_utilities = validation::get_all_utilities(util_map);
6670

6771
let matches = Command::new("manpage")
@@ -78,7 +82,13 @@ fn gen_manpage<T: Args>(args: impl Iterator<Item = OsString>, util_map: &Utility
7882
gen_coreutils_app(util_map)
7983
} else {
8084
validation::setup_localization_or_exit(utility);
81-
util_map.get(utility).unwrap().1()
85+
let mut cmd = util_map.get(utility).unwrap().1();
86+
if let Some(zip) = tldr {
87+
if let Ok(examples) = write_zip_examples(zip, utility, false) {
88+
cmd = cmd.after_help(examples);
89+
}
90+
}
91+
cmd
8292
};
8393

8494
let man = Man::new(command);
@@ -122,19 +132,41 @@ fn gen_completions<T: Args>(args: impl Iterator<Item = OsString>, util_map: &Uti
122132
process::exit(0);
123133
}
124134

135+
/// print tldr error
136+
fn print_tldr_error() {
137+
eprintln!("Warning: No tldr archive found, so the documentation will not include examples.");
138+
eprintln!(
139+
"To include examples in the documentation, download the tldr archive and put it in the docs/ folder."
140+
);
141+
eprintln!();
142+
eprintln!(" curl https://tldr.sh/assets/tldr.zip -o docs/tldr.zip");
143+
eprintln!();
144+
}
145+
125146
/// # Errors
126147
/// Returns an error if the writer fails.
127148
#[allow(clippy::too_many_lines)]
128149
fn main() -> io::Result<()> {
129150
let args: Vec<OsString> = uucore::args_os_filtered().collect();
130151

152+
let mut tldr_zip = File::open("docs/tldr.zip")
153+
.ok()
154+
.and_then(|f| ZipArchive::new(f).ok());
155+
131156
// Check for manpage/completion commands first
132157
if args.len() > 1 {
133158
let command = args.get(1).and_then(|s| s.to_str()).unwrap_or_default();
134159
match command {
135160
"manpage" => {
136161
let args_iter = args.into_iter().skip(2);
137-
gen_manpage(args_iter, &util_map::<Box<dyn Iterator<Item = OsString>>>());
162+
if tldr_zip.is_none() {
163+
print_tldr_error();
164+
}
165+
gen_manpage(
166+
&mut tldr_zip,
167+
args_iter,
168+
&util_map::<Box<dyn Iterator<Item = OsString>>>(),
169+
);
138170
}
139171
"completion" => {
140172
let args_iter = args.into_iter().skip(2);
@@ -151,20 +183,9 @@ fn main() -> io::Result<()> {
151183
}
152184
}
153185
}
154-
let mut tldr_zip = File::open("docs/tldr.zip")
155-
.ok()
156-
.and_then(|f| ZipArchive::new(f).ok());
157-
158186
if tldr_zip.is_none() {
159-
println!("Warning: No tldr archive found, so the documentation will not include examples.");
160-
println!(
161-
"To include examples in the documentation, download the tldr archive and put it in the docs/ folder."
162-
);
163-
println!();
164-
println!(" curl https://tldr.sh/assets/tldr.zip -o docs/tldr.zip");
165-
println!();
187+
print_tldr_error();
166188
}
167-
168189
let utils = util_map::<Box<dyn Iterator<Item = OsString>>>();
169190
match std::fs::create_dir("docs/src/utils/") {
170191
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => Ok(()),
@@ -431,44 +452,9 @@ impl MDWriter<'_, '_> {
431452
/// Returns an error if the writer fails.
432453
fn examples(&mut self) -> io::Result<()> {
433454
if let Some(zip) = self.tldr_zip {
434-
let content = if let Some(f) =
435-
get_zip_content(zip, &format!("pages/common/{}.md", self.name))
436-
{
437-
f
438-
} else if let Some(f) = get_zip_content(zip, &format!("pages/linux/{}.md", self.name)) {
439-
f
440-
} else {
441-
println!(
442-
"Warning: Could not find tldr examples for page '{}'",
443-
self.name
444-
);
445-
return Ok(());
446-
};
447-
448-
writeln!(self.w, "## Examples")?;
449-
writeln!(self.w)?;
450-
for line in content.lines().skip_while(|l| !l.starts_with('-')) {
451-
if let Some(l) = line.strip_prefix("- ") {
452-
writeln!(self.w, "{l}")?;
453-
} else if line.starts_with('`') {
454-
writeln!(self.w, "```shell\n{}\n```", line.trim_matches('`'))?;
455-
} else if line.is_empty() {
456-
writeln!(self.w)?;
457-
} else {
458-
println!("Not sure what to do with this line:");
459-
println!("{line}");
460-
}
455+
if let Ok(examples) = write_zip_examples(zip, self.name, true) {
456+
writeln!(self.w, "{examples}")?;
461457
}
462-
writeln!(self.w)?;
463-
writeln!(
464-
self.w,
465-
"> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)."
466-
)?;
467-
writeln!(self.w, ">")?;
468-
writeln!(
469-
self.w,
470-
"> Please note that, as uutils is a work in progress, some examples might fail."
471-
)?;
472458
}
473459
Ok(())
474460
}
@@ -548,3 +534,72 @@ fn get_zip_content(archive: &mut ZipArchive<impl Read + Seek>, name: &str) -> Op
548534
archive.by_name(name).ok()?.read_to_string(&mut s).unwrap();
549535
Some(s)
550536
}
537+
538+
/// Extract examples for tldr.zip. The file docs/tldr.zip must exists
539+
///
540+
/// ```sh
541+
/// curl https://tldr.sh/assets/tldr.zip -o docs/tldr.zip
542+
/// ```
543+
///
544+
/// # Errors
545+
///
546+
/// Returns an error if the tldr.zip file cannot be opened or read
547+
fn write_zip_examples(
548+
archive: &mut ZipArchive<impl Read + Seek>,
549+
name: &str,
550+
output_markdown: bool,
551+
) -> io::Result<String> {
552+
let content = if let Some(f) = get_zip_content(archive, &format!("pages/common/{name}.md")) {
553+
f
554+
} else if let Some(f) = get_zip_content(archive, &format!("pages/linux/{name}.md")) {
555+
f
556+
} else {
557+
return Err(io::Error::new(
558+
io::ErrorKind::NotFound,
559+
format!("Could not find tldr examples for {name}"),
560+
));
561+
};
562+
563+
match format_examples(content, output_markdown) {
564+
Err(e) => Err(std::io::Error::other(format!(
565+
"Failed to format the tldr examples of {name}: {e}"
566+
))),
567+
Ok(s) => Ok(s),
568+
}
569+
}
570+
571+
/// Format examples using std::fmt::Write
572+
fn format_examples(content: String, output_markdown: bool) -> Result<String, std::fmt::Error> {
573+
use std::fmt::Write;
574+
let mut s = String::new();
575+
writeln!(s)?;
576+
writeln!(s, "Examples")?;
577+
writeln!(s)?;
578+
for line in content.lines().skip_while(|l| !l.starts_with('-')) {
579+
if let Some(l) = line.strip_prefix("- ") {
580+
writeln!(s, "{l}")?;
581+
} else if line.starts_with('`') {
582+
if output_markdown {
583+
writeln!(s, "```shell\n{}\n```", line.trim_matches('`'))?;
584+
} else {
585+
writeln!(s, "{}", line.trim_matches('`'))?;
586+
}
587+
} else if line.is_empty() {
588+
writeln!(s)?;
589+
} else {
590+
// println!("Not sure what to do with this line:");
591+
// println!("{line}");
592+
}
593+
}
594+
writeln!(s)?;
595+
writeln!(
596+
s,
597+
"> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)."
598+
)?;
599+
writeln!(s, ">")?;
600+
writeln!(
601+
s,
602+
"> Please note that, as uutils is a work in progress, some examples might fail."
603+
)?;
604+
Ok(s)
605+
}

tests/uudoc/mod.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@ fn test_manpage_generation() {
2929
output.status
3030
);
3131
assert!(
32-
output.stderr.is_empty(),
33-
"stderr should be empty but got: {}",
34-
String::from_utf8_lossy(&output.stderr)
32+
String::from_utf8_lossy(&output.stderr).contains("Warning: No tldr archive found"),
33+
"stderr should contains tldr alert",
3534
);
3635

3736
let output_str = String::from_utf8_lossy(&output.stdout);
@@ -53,9 +52,8 @@ fn test_manpage_coreutils() {
5352
output.status
5453
);
5554
assert!(
56-
output.stderr.is_empty(),
57-
"stderr should be empty but got: {}",
58-
String::from_utf8_lossy(&output.stderr)
55+
String::from_utf8_lossy(&output.stderr).contains("Warning: No tldr archive found"),
56+
"stderr should contains tldr alert",
5957
);
6058

6159
let output_str = String::from_utf8_lossy(&output.stdout);
@@ -124,9 +122,8 @@ fn test_manpage_base64() {
124122
output.status
125123
);
126124
assert!(
127-
output.stderr.is_empty(),
128-
"stderr should be empty but got: {}",
129-
String::from_utf8_lossy(&output.stderr)
125+
String::from_utf8_lossy(&output.stderr).contains("Warning: No tldr archive found"),
126+
"stderr should contains tldr alert",
130127
);
131128

132129
let output_str = String::from_utf8_lossy(&output.stdout);

0 commit comments

Comments
 (0)