mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
Add example to manpage (#7841)
This commit is contained in:
+1
-1
@@ -31,7 +31,7 @@ expensive_tests = []
|
||||
# "test_risky_names" == enable tests that create problematic file names (would make a network share inaccessible to Windows, breaks SVN on Mac OS, etc.)
|
||||
test_risky_names = []
|
||||
# * only build `uudoc` when `--feature uudoc` is activated
|
||||
uudoc = ["clap_complete", "clap_mangen", "fluent-syntax", "zip"]
|
||||
uudoc = ["dep:clap_complete", "dep:clap_mangen", "dep:fluent-syntax", "dep:zip"]
|
||||
## features
|
||||
## Optional feature for stdbuf
|
||||
# "feat_external_libstdbuf" == use an external libstdbuf.so for stdbuf instead of embedding it
|
||||
|
||||
+10
-5
@@ -3,15 +3,13 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use clap::Command;
|
||||
use coreutils::validation;
|
||||
use std::cmp;
|
||||
use std::ffi::OsString;
|
||||
use std::io::{self, Write};
|
||||
use std::process;
|
||||
|
||||
use clap::Command;
|
||||
|
||||
use coreutils::validation;
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"));
|
||||
@@ -19,7 +17,14 @@ include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"));
|
||||
fn usage<T>(utils: &UtilityMap<T>, name: &str) {
|
||||
println!("{name} {VERSION} (multi-call binary)\n");
|
||||
println!("Usage: {name} [function [arguments...]]");
|
||||
println!(" {name} --list\n");
|
||||
println!(" {name} --list");
|
||||
println!();
|
||||
#[cfg(feature = "feat_common_core")]
|
||||
{
|
||||
println!("Functions:");
|
||||
println!(" '<uutils>' [arguments...]");
|
||||
println!();
|
||||
}
|
||||
println!("Options:");
|
||||
println!(" --list lists all defined functions, one per row\n");
|
||||
println!("Currently defined functions:\n");
|
||||
|
||||
+107
-52
@@ -61,7 +61,11 @@ fn gen_coreutils_app<T: Args>(util_map: &UtilityMap<T>) -> clap::Command {
|
||||
}
|
||||
|
||||
/// Generate the manpage for the utility in the first parameter
|
||||
fn gen_manpage<T: Args>(args: impl Iterator<Item = OsString>, util_map: &UtilityMap<T>) -> ! {
|
||||
fn gen_manpage<T: Args>(
|
||||
tldr: &mut Option<ZipArchive<File>>,
|
||||
args: impl Iterator<Item = OsString>,
|
||||
util_map: &UtilityMap<T>,
|
||||
) -> ! {
|
||||
let all_utilities = validation::get_all_utilities(util_map);
|
||||
|
||||
let matches = Command::new("manpage")
|
||||
@@ -78,7 +82,13 @@ fn gen_manpage<T: Args>(args: impl Iterator<Item = OsString>, util_map: &Utility
|
||||
gen_coreutils_app(util_map)
|
||||
} else {
|
||||
validation::setup_localization_or_exit(utility);
|
||||
util_map.get(utility).unwrap().1()
|
||||
let mut cmd = util_map.get(utility).unwrap().1();
|
||||
if let Some(zip) = tldr {
|
||||
if let Ok(examples) = write_zip_examples(zip, utility, false) {
|
||||
cmd = cmd.after_help(examples);
|
||||
}
|
||||
}
|
||||
cmd
|
||||
};
|
||||
|
||||
let man = Man::new(command);
|
||||
@@ -122,19 +132,41 @@ fn gen_completions<T: Args>(args: impl Iterator<Item = OsString>, util_map: &Uti
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
/// print tldr error
|
||||
fn print_tldr_error() {
|
||||
eprintln!("Warning: No tldr archive found, so the documentation will not include examples.");
|
||||
eprintln!(
|
||||
"To include examples in the documentation, download the tldr archive and put it in the docs/ folder."
|
||||
);
|
||||
eprintln!();
|
||||
eprintln!(" curl https://tldr.sh/assets/tldr.zip -o docs/tldr.zip");
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
/// Returns an error if the writer fails.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn main() -> io::Result<()> {
|
||||
let args: Vec<OsString> = uucore::args_os_filtered().collect();
|
||||
|
||||
let mut tldr_zip = File::open("docs/tldr.zip")
|
||||
.ok()
|
||||
.and_then(|f| ZipArchive::new(f).ok());
|
||||
|
||||
// Check for manpage/completion commands first
|
||||
if args.len() > 1 {
|
||||
let command = args.get(1).and_then(|s| s.to_str()).unwrap_or_default();
|
||||
match command {
|
||||
"manpage" => {
|
||||
let args_iter = args.into_iter().skip(2);
|
||||
gen_manpage(args_iter, &util_map::<Box<dyn Iterator<Item = OsString>>>());
|
||||
if tldr_zip.is_none() {
|
||||
print_tldr_error();
|
||||
}
|
||||
gen_manpage(
|
||||
&mut tldr_zip,
|
||||
args_iter,
|
||||
&util_map::<Box<dyn Iterator<Item = OsString>>>(),
|
||||
);
|
||||
}
|
||||
"completion" => {
|
||||
let args_iter = args.into_iter().skip(2);
|
||||
@@ -151,20 +183,9 @@ fn main() -> io::Result<()> {
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut tldr_zip = File::open("docs/tldr.zip")
|
||||
.ok()
|
||||
.and_then(|f| ZipArchive::new(f).ok());
|
||||
|
||||
if tldr_zip.is_none() {
|
||||
println!("Warning: No tldr archive found, so the documentation will not include examples.");
|
||||
println!(
|
||||
"To include examples in the documentation, download the tldr archive and put it in the docs/ folder."
|
||||
);
|
||||
println!();
|
||||
println!(" curl https://tldr.sh/assets/tldr.zip -o docs/tldr.zip");
|
||||
println!();
|
||||
print_tldr_error();
|
||||
}
|
||||
|
||||
let utils = util_map::<Box<dyn Iterator<Item = OsString>>>();
|
||||
match std::fs::create_dir("docs/src/utils/") {
|
||||
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => Ok(()),
|
||||
@@ -431,44 +452,9 @@ impl MDWriter<'_, '_> {
|
||||
/// Returns an error if the writer fails.
|
||||
fn examples(&mut self) -> io::Result<()> {
|
||||
if let Some(zip) = self.tldr_zip {
|
||||
let content = if let Some(f) =
|
||||
get_zip_content(zip, &format!("pages/common/{}.md", self.name))
|
||||
{
|
||||
f
|
||||
} else if let Some(f) = get_zip_content(zip, &format!("pages/linux/{}.md", self.name)) {
|
||||
f
|
||||
} else {
|
||||
println!(
|
||||
"Warning: Could not find tldr examples for page '{}'",
|
||||
self.name
|
||||
);
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
writeln!(self.w, "## Examples")?;
|
||||
writeln!(self.w)?;
|
||||
for line in content.lines().skip_while(|l| !l.starts_with('-')) {
|
||||
if let Some(l) = line.strip_prefix("- ") {
|
||||
writeln!(self.w, "{l}")?;
|
||||
} else if line.starts_with('`') {
|
||||
writeln!(self.w, "```shell\n{}\n```", line.trim_matches('`'))?;
|
||||
} else if line.is_empty() {
|
||||
writeln!(self.w)?;
|
||||
} else {
|
||||
println!("Not sure what to do with this line:");
|
||||
println!("{line}");
|
||||
}
|
||||
if let Ok(examples) = write_zip_examples(zip, self.name, true) {
|
||||
writeln!(self.w, "{examples}")?;
|
||||
}
|
||||
writeln!(self.w)?;
|
||||
writeln!(
|
||||
self.w,
|
||||
"> 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)."
|
||||
)?;
|
||||
writeln!(self.w, ">")?;
|
||||
writeln!(
|
||||
self.w,
|
||||
"> Please note that, as uutils is a work in progress, some examples might fail."
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -548,3 +534,72 @@ fn get_zip_content(archive: &mut ZipArchive<impl Read + Seek>, name: &str) -> Op
|
||||
archive.by_name(name).ok()?.read_to_string(&mut s).unwrap();
|
||||
Some(s)
|
||||
}
|
||||
|
||||
/// Extract examples for tldr.zip. The file docs/tldr.zip must exists
|
||||
///
|
||||
/// ```sh
|
||||
/// curl https://tldr.sh/assets/tldr.zip -o docs/tldr.zip
|
||||
/// ```
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the tldr.zip file cannot be opened or read
|
||||
fn write_zip_examples(
|
||||
archive: &mut ZipArchive<impl Read + Seek>,
|
||||
name: &str,
|
||||
output_markdown: bool,
|
||||
) -> io::Result<String> {
|
||||
let content = if let Some(f) = get_zip_content(archive, &format!("pages/common/{name}.md")) {
|
||||
f
|
||||
} else if let Some(f) = get_zip_content(archive, &format!("pages/linux/{name}.md")) {
|
||||
f
|
||||
} else {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
format!("Could not find tldr examples for {name}"),
|
||||
));
|
||||
};
|
||||
|
||||
match format_examples(content, output_markdown) {
|
||||
Err(e) => Err(std::io::Error::other(format!(
|
||||
"Failed to format the tldr examples of {name}: {e}"
|
||||
))),
|
||||
Ok(s) => Ok(s),
|
||||
}
|
||||
}
|
||||
|
||||
/// Format examples using std::fmt::Write
|
||||
fn format_examples(content: String, output_markdown: bool) -> Result<String, std::fmt::Error> {
|
||||
use std::fmt::Write;
|
||||
let mut s = String::new();
|
||||
writeln!(s)?;
|
||||
writeln!(s, "Examples")?;
|
||||
writeln!(s)?;
|
||||
for line in content.lines().skip_while(|l| !l.starts_with('-')) {
|
||||
if let Some(l) = line.strip_prefix("- ") {
|
||||
writeln!(s, "{l}")?;
|
||||
} else if line.starts_with('`') {
|
||||
if output_markdown {
|
||||
writeln!(s, "```shell\n{}\n```", line.trim_matches('`'))?;
|
||||
} else {
|
||||
writeln!(s, "{}", line.trim_matches('`'))?;
|
||||
}
|
||||
} else if line.is_empty() {
|
||||
writeln!(s)?;
|
||||
} else {
|
||||
// println!("Not sure what to do with this line:");
|
||||
// println!("{line}");
|
||||
}
|
||||
}
|
||||
writeln!(s)?;
|
||||
writeln!(
|
||||
s,
|
||||
"> 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)."
|
||||
)?;
|
||||
writeln!(s, ">")?;
|
||||
writeln!(
|
||||
s,
|
||||
"> Please note that, as uutils is a work in progress, some examples might fail."
|
||||
)?;
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
+6
-9
@@ -29,9 +29,8 @@ fn test_manpage_generation() {
|
||||
output.status
|
||||
);
|
||||
assert!(
|
||||
output.stderr.is_empty(),
|
||||
"stderr should be empty but got: {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
String::from_utf8_lossy(&output.stderr).contains("Warning: No tldr archive found"),
|
||||
"stderr should contains tldr alert",
|
||||
);
|
||||
|
||||
let output_str = String::from_utf8_lossy(&output.stdout);
|
||||
@@ -53,9 +52,8 @@ fn test_manpage_coreutils() {
|
||||
output.status
|
||||
);
|
||||
assert!(
|
||||
output.stderr.is_empty(),
|
||||
"stderr should be empty but got: {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
String::from_utf8_lossy(&output.stderr).contains("Warning: No tldr archive found"),
|
||||
"stderr should contains tldr alert",
|
||||
);
|
||||
|
||||
let output_str = String::from_utf8_lossy(&output.stdout);
|
||||
@@ -124,9 +122,8 @@ fn test_manpage_base64() {
|
||||
output.status
|
||||
);
|
||||
assert!(
|
||||
output.stderr.is_empty(),
|
||||
"stderr should be empty but got: {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
String::from_utf8_lossy(&output.stderr).contains("Warning: No tldr archive found"),
|
||||
"stderr should contains tldr alert",
|
||||
);
|
||||
|
||||
let output_str = String::from_utf8_lossy(&output.stdout);
|
||||
|
||||
Reference in New Issue
Block a user