Merge pull request #4385 from cakebaker/uucore_procs_help_about

uucore_procs: extract "about" and "usage" info from new help structure
This commit is contained in:
Sylvestre Ledru 2023-02-19 11:47:18 +01:00 committed by GitHub
commit f77a44df8b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 133 additions and 74 deletions

View file

@ -1,12 +1,9 @@
# base32
## Usage
```
base32 [OPTION]... [FILE]
```
## About
encode/decode data and print to standard output
With no FILE, or when FILE is -, read standard input.

View file

@ -8,11 +8,11 @@
use std::io::{stdin, Read};
use clap::Command;
use uucore::{encoding::Format, error::UResult, help_section, help_usage};
use uucore::{encoding::Format, error::UResult, help_about, help_usage};
pub mod base_common;
const ABOUT: &str = help_section!("about", "base32.md");
const ABOUT: &str = help_about!("base32.md");
const USAGE: &str = help_usage!("base32.md");
#[uucore::main]

View file

@ -1,12 +1,9 @@
# base64
## Usage
```
base64 [OPTION]... [FILE]
```
## About
encode/decode data and print to standard output
With no FILE, or when FILE is -, read standard input.

View file

@ -9,11 +9,11 @@
use uu_base32::base_common;
pub use uu_base32::uu_app;
use uucore::{encoding::Format, error::UResult, help_section, help_usage};
use uucore::{encoding::Format, error::UResult, help_about, help_usage};
use std::io::{stdin, Read};
const ABOUT: &str = help_section!("about", "base64.md");
const ABOUT: &str = help_about!("base64.md");
const USAGE: &str = help_usage!("base64.md");
#[uucore::main]

View file

@ -1,11 +1,8 @@
# cat
## Usage
```
cat [OPTION]... [FILE]...
```
## About
Concatenate FILE(s), or standard input, to standard output
With no FILE, or when FILE is -, read standard input.

View file

@ -33,10 +33,10 @@ use std::net::Shutdown;
use std::os::unix::fs::FileTypeExt;
#[cfg(unix)]
use std::os::unix::net::UnixStream;
use uucore::{format_usage, help_section, help_usage};
use uucore::{format_usage, help_about, help_usage};
const USAGE: &str = help_usage!("cat.md");
const ABOUT: &str = help_section!("about", "cat.md");
const ABOUT: &str = help_about!("cat.md");
#[derive(Error, Debug)]
enum CatError {

View file

@ -1,12 +1,9 @@
# cp
## Usage
```
cp [OPTION]... [-T] SOURCE DEST
cp [OPTION]... SOURCE... DIRECTORY
cp [OPTION]... -t DIRECTORY SOURCE...
```
## About
Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.

View file

@ -40,7 +40,7 @@ use uucore::error::{set_exit_code, UClapError, UError, UResult, UUsageError};
use uucore::fs::{
canonicalize, paths_refer_to_same_file, FileInformation, MissingHandling, ResolveMode,
};
use uucore::{crash, format_usage, help_section, help_usage, prompt_yes, show_error, show_warning};
use uucore::{crash, format_usage, help_about, help_usage, prompt_yes, show_error, show_warning};
use crate::copydir::copy_directory;
@ -228,11 +228,11 @@ pub struct Options {
progress_bar: bool,
}
const ABOUT: &str = help_section!("about", "cp.md");
static EXIT_ERR: i32 = 1;
const ABOUT: &str = help_about!("cp.md");
const USAGE: &str = help_usage!("cp.md");
static EXIT_ERR: i32 = 1;
// Argument constants
mod options {
pub const ARCHIVE: &str = "archive";

View file

@ -1,7 +1,11 @@
<!-- spell-checker:ignore convs iseek oseek -->
# dd
## About
```
dd [OPERAND]...
dd OPTION
```
Copy, and optionally convert, a file system resource
## After Help

View file

@ -39,11 +39,11 @@ use clap::{crate_version, Arg, Command};
use gcd::Gcd;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult};
use uucore::help_section;
use uucore::show_error;
use uucore::{format_usage, help_about, help_section, help_usage, show_error};
const ABOUT: &str = help_section!("about", "dd.md");
const ABOUT: &str = help_about!("dd.md");
const AFTER_HELP: &str = help_section!("after help", "dd.md");
const USAGE: &str = help_usage!("dd.md");
const BUF_INIT_BYTE: u8 = 0xDD;
/// Final settings after parsing
@ -832,6 +832,7 @@ pub fn uu_app() -> Command {
Command::new(uucore::util_name())
.version(crate_version!())
.about(ABOUT)
.override_usage(format_usage(USAGE))
.after_help(AFTER_HELP)
.infer_long_args(true)
.arg(Arg::new(options::OPERANDS).num_args(1..))

View file

@ -1,15 +1,12 @@
# expr
## About
Print the value of `EXPRESSION` to standard output
## Usage
```
expr [EXPRESSION]
expr [OPTIONS]
```
Print the value of `EXPRESSION` to standard output
## After help
Print the value of `EXPRESSION` to standard output. A blank line below
@ -58,4 +55,4 @@ Environment variables:
- `EXPR_DEBUG_TOKENS=1`: dump expression's tokens
- `EXPR_DEBUG_RPN=1`: dump expression represented in reverse polish notation
- `EXPR_DEBUG_SYA_STEP=1`: dump each parser step
- `EXPR_DEBUG_AST=1`: dump expression represented abstract syntax tree
- `EXPR_DEBUG_AST=1`: dump expression represented abstract syntax tree

View file

@ -8,7 +8,7 @@
use clap::{crate_version, Arg, ArgAction, Command};
use uucore::{
error::{UResult, USimpleError},
format_usage, help_section, help_usage,
format_usage, help_about, help_section, help_usage,
};
mod syntax_tree;
@ -23,7 +23,7 @@ mod options {
pub fn uu_app() -> Command {
Command::new(uucore::util_name())
.version(crate_version!())
.about(help_section!("about", "expr.md"))
.about(help_about!("expr.md"))
.override_usage(format_usage(help_usage!("expr.md")))
.after_help(help_section!("after help", "expr.md"))
.infer_long_args(true)

View file

@ -1,13 +1,10 @@
<!-- spell-checker:ignore N'th M'th -->
# numfmt
## Usage
```
numfmt [OPTION]... [NUMBER]...
```
## About
Convert numbers from/to human-readable strings
## After Help

View file

@ -14,16 +14,15 @@ use std::io::{BufRead, Write};
use units::{IEC_BASES, SI_BASES};
use uucore::display::Quotable;
use uucore::error::UResult;
use uucore::format_usage;
use uucore::ranges::Range;
use uucore::{help_section, help_usage};
use uucore::{format_usage, help_about, help_section, help_usage};
pub mod errors;
pub mod format;
pub mod options;
mod units;
const ABOUT: &str = help_section!("about", "numfmt.md");
const ABOUT: &str = help_about!("numfmt.md");
const AFTER_HELP: &str = help_section!("after help", "numfmt.md");
const USAGE: &str = help_usage!("numfmt.md");

View file

@ -13,7 +13,9 @@ use uucore::fsext::{
pretty_filetype, pretty_fstype, pretty_time, read_fs_list, statfs, BirthTime, FsMeta,
};
use uucore::libc::mode_t;
use uucore::{entries, format_usage, help_section, help_usage, show_error, show_warning};
use uucore::{
entries, format_usage, help_about, help_section, help_usage, show_error, show_warning,
};
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
use std::borrow::Cow;
@ -24,7 +26,7 @@ use std::os::unix::fs::{FileTypeExt, MetadataExt};
use std::os::unix::prelude::OsStrExt;
use std::path::Path;
const ABOUT: &str = help_section!("about", "stat.md");
const ABOUT: &str = help_about!("stat.md");
const USAGE: &str = help_usage!("stat.md");
const LONG_USAGE: &str = help_section!("long usage", "stat.md");

View file

@ -1,14 +1,11 @@
# stat
## About
Display file or file system status.
## Usage
```
stat [OPTION]... FILE...
```
Display file or file system status.
## Long Usage
The valid format sequences for files (without `--file-system`):

View file

@ -7,6 +7,8 @@ use std::{fs::File, io::Read, path::PathBuf};
use proc_macro::{Literal, TokenStream, TokenTree};
use quote::quote;
const MARKDOWN_CODE_FENCES: &str = "```";
//## rust proc-macro background info
//* ref: <https://dev.to/naufraghi/procedural-macro-in-rust-101-k3f> @@ <http://archive.is/Vbr5e>
//* ref: [path construction from LitStr](https://oschwald.github.io/maxminddb-rust/syn/struct.LitStr.html) @@ <http://archive.is/8YDua>
@ -51,7 +53,19 @@ fn render_markdown(s: &str) -> String {
s.replace('`', "")
}
/// Get the usage from the "Usage" section in the help file.
/// Get the about text from the help file.
///
/// The about text is assumed to be the text between the first markdown
/// code block and the next header, if any. It may span multiple lines.
#[proc_macro]
pub fn help_about(input: TokenStream) -> TokenStream {
let input: Vec<TokenTree> = input.into_iter().collect();
let filename = get_argument(&input, 0, "filename");
let text: String = parse_about(&read_help(&filename));
TokenTree::Literal(Literal::string(&text)).into()
}
/// Get the usage from the help file.
///
/// The usage is assumed to be surrounded by markdown code fences. It may span
/// multiple lines. The first word of each line is assumed to be the name of
@ -61,7 +75,7 @@ fn render_markdown(s: &str) -> String {
pub fn help_usage(input: TokenStream) -> TokenStream {
let input: Vec<TokenTree> = input.into_iter().collect();
let filename = get_argument(&input, 0, "filename");
let text: String = parse_usage(&parse_help("usage", &filename));
let text: String = parse_usage(&read_help(&filename));
TokenTree::Literal(Literal::string(&text)).into()
}
@ -94,7 +108,7 @@ pub fn help_section(input: TokenStream) -> TokenStream {
let input: Vec<TokenTree> = input.into_iter().collect();
let section = get_argument(&input, 0, "section");
let filename = get_argument(&input, 1, "filename");
let text = parse_help(&section, &filename);
let text = parse_help_section(&section, &read_help(&filename));
let rendered = render_markdown(&text);
TokenTree::Literal(Literal::string(&rendered)).into()
}
@ -121,13 +135,11 @@ fn get_argument(input: &[TokenTree], index: usize, name: &str) -> String {
.to_string()
}
/// Read the help file and extract a section
fn parse_help(section: &str, filename: &str) -> String {
let section = section.to_lowercase();
let section = section.trim_matches('"');
/// Read the help file
fn read_help(filename: &str) -> String {
let mut content = String::new();
let mut path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
let mut path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
path.push(filename);
File::open(path)
@ -135,7 +147,7 @@ fn parse_help(section: &str, filename: &str) -> String {
.read_to_string(&mut content)
.unwrap();
parse_help_section(section, &content)
content
}
/// Get a single section from content
@ -147,6 +159,8 @@ fn parse_help_section(section: &str, content: &str) -> String {
.map_or(false, |l| l.trim().to_lowercase() == section)
}
let section = &section.to_lowercase();
// We cannot distinguish between an empty or non-existing section below,
// so we do a quick test to check whether the section exists to provide
// a nice error message.
@ -167,17 +181,17 @@ fn parse_help_section(section: &str, content: &str) -> String {
.to_string()
}
/// Parses a markdown code block into a usage string
/// Parses the first markdown code block into a usage string
///
/// The code fences are removed and the name of the util is replaced
/// with `{}` so that it can be replaced with the appropriate name
/// at runtime.
fn parse_usage(content: &str) -> String {
content
.strip_suffix("```")
.unwrap()
.lines()
.skip(1) // Skip the "```" of markdown syntax
.skip_while(|l| !l.starts_with(MARKDOWN_CODE_FENCES))
.skip(1)
.take_while(|l| !l.starts_with(MARKDOWN_CODE_FENCES))
.map(|l| {
// Replace the util name (assumed to be the first word) with "{}"
// to be replaced with the runtime value later.
@ -187,12 +201,31 @@ fn parse_usage(content: &str) -> String {
"{}\n".to_string()
}
})
.collect()
.collect::<Vec<_>>()
.join("")
.trim()
.to_string()
}
/// Parses the text between the first markdown code block and the next header, if any,
/// into an about string.
fn parse_about(content: &str) -> String {
content
.lines()
.skip_while(|l| !l.starts_with(MARKDOWN_CODE_FENCES))
.skip(1)
.skip_while(|l| !l.starts_with(MARKDOWN_CODE_FENCES))
.skip(1)
.take_while(|l| !l.starts_with('#'))
.collect::<Vec<_>>()
.join("\n")
.trim()
.to_string()
}
#[cfg(test)]
mod tests {
use super::{parse_help_section, parse_usage};
use super::{parse_about, parse_help_section, parse_usage};
#[test]
fn section_parsing() {
@ -209,6 +242,10 @@ mod tests {
parse_help_section("some section", input),
"This is some section"
);
assert_eq!(
parse_help_section("SOME SECTION", input),
"This is some section"
);
assert_eq!(
parse_help_section("another section", input),
"This is the other section\nwith multiple lines"
@ -233,7 +270,6 @@ mod tests {
fn usage_parsing() {
let input = "\
# ls\n\
## Usage\n\
```\n\
ls -l\n\
```\n\
@ -244,17 +280,55 @@ mod tests {
This is the other section\n\
with multiple lines\n";
assert_eq!(parse_usage(&parse_help_section("usage", input)), "{} -l",);
assert_eq!(parse_usage(input), "{} -l");
}
assert_eq!(
parse_usage(
"\
```\n\
util [some] [options]\n\
```\
"
),
"{} [some] [options]"
);
#[test]
fn multi_line_usage_parsing() {
let input = "\
# ls\n\
```\n\
ls -a\n\
ls -b\n\
ls -c\n\
```\n\
## some section\n\
This is some section\n";
assert_eq!(parse_usage(input), "{} -a\n{} -b\n{} -c");
}
#[test]
fn about_parsing() {
let input = "\
# ls\n\
```\n\
ls -l\n\
```\n\
\n\
This is the about section\n\
\n\
## some section\n\
This is some section\n";
assert_eq!(parse_about(input), "This is the about section");
}
#[test]
fn multi_line_about_parsing() {
let input = "\
# ls\n\
```\n\
ls -l\n\
```\n\
\n\
about a\n\
\n\
about b\n\
\n\
## some section\n\
This is some section\n";
assert_eq!(parse_about(input), "about a\n\nabout b");
}
}