From 94eddc470152e8f957322d1a1f70c55f83639ace Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 17 Feb 2023 09:25:32 +0100 Subject: [PATCH 1/4] uucore_procs: separate file opening & parsing --- src/uucore_procs/src/lib.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/uucore_procs/src/lib.rs b/src/uucore_procs/src/lib.rs index 1a452254a..dc3d0fbc0 100644 --- a/src/uucore_procs/src/lib.rs +++ b/src/uucore_procs/src/lib.rs @@ -61,7 +61,7 @@ fn render_markdown(s: &str) -> String { pub fn help_usage(input: TokenStream) -> TokenStream { let input: Vec = 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(&parse_help_section("usage", &read_help(&filename))); TokenTree::Literal(Literal::string(&text)).into() } @@ -94,7 +94,7 @@ pub fn help_section(input: TokenStream) -> TokenStream { let input: Vec = input.into_iter().collect(); let section = get_argument(&input, 0, "section"); let filename = get_argument(&input, 1, "filename"); - let text = parse_help(§ion, &filename); + let text = parse_help_section(§ion, &read_help(&filename)); let rendered = render_markdown(&text); TokenTree::Literal(Literal::string(&rendered)).into() } @@ -121,13 +121,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 +133,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 +145,8 @@ fn parse_help_section(section: &str, content: &str) -> String { .map_or(false, |l| l.trim().to_lowercase() == section) } + let section = §ion.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. @@ -209,6 +209,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" From 1ba0c6853c854fbd5765fd86e2df02ef7d42ead1 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 17 Feb 2023 14:51:40 +0100 Subject: [PATCH 2/4] uucore_procs: drop need for "Usage" header in help --- src/uucore_procs/src/lib.rs | 46 ++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/uucore_procs/src/lib.rs b/src/uucore_procs/src/lib.rs index dc3d0fbc0..cb7e4c355 100644 --- a/src/uucore_procs/src/lib.rs +++ b/src/uucore_procs/src/lib.rs @@ -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: @@ //* ref: [path construction from LitStr](https://oschwald.github.io/maxminddb-rust/syn/struct.LitStr.html) @@ @@ -51,7 +53,7 @@ fn render_markdown(s: &str) -> String { s.replace('`', "") } -/// Get the usage from the "Usage" section in the help file. +/// 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 +63,7 @@ fn render_markdown(s: &str) -> String { pub fn help_usage(input: TokenStream) -> TokenStream { let input: Vec = input.into_iter().collect(); let filename = get_argument(&input, 0, "filename"); - let text: String = parse_usage(&parse_help_section("usage", &read_help(&filename))); + let text: String = parse_usage(&read_help(&filename)); TokenTree::Literal(Literal::string(&text)).into() } @@ -167,17 +169,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,7 +189,10 @@ fn parse_usage(content: &str) -> String { "{}\n".to_string() } }) - .collect() + .collect::>() + .join("") + .trim() + .to_string() } #[cfg(test)] @@ -237,7 +242,6 @@ mod tests { fn usage_parsing() { let input = "\ # ls\n\ - ## Usage\n\ ```\n\ ls -l\n\ ```\n\ @@ -248,17 +252,21 @@ 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"); } } From 2c027a9312ab1063311294b9894cc54c9f4afd18 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 17 Feb 2023 15:39:26 +0100 Subject: [PATCH 3/4] uucore_procs: add help_about macro --- src/uucore_procs/src/lib.rs | 64 ++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/src/uucore_procs/src/lib.rs b/src/uucore_procs/src/lib.rs index cb7e4c355..c1ef8bc75 100644 --- a/src/uucore_procs/src/lib.rs +++ b/src/uucore_procs/src/lib.rs @@ -53,6 +53,18 @@ fn render_markdown(s: &str) -> String { s.replace('`', "") } +/// 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 = 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 @@ -195,9 +207,25 @@ fn parse_usage(content: &str) -> String { .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::>() + .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() { @@ -269,4 +297,38 @@ mod tests { 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"); + } } From dcc7c4ed7b120a95acc4a28c6db08c2fb4ffcd93 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 17 Feb 2023 16:40:22 +0100 Subject: [PATCH 4/4] Use help_about! & adapt help files for its usage --- src/uu/base32/base32.md | 3 --- src/uu/base32/src/base32.rs | 4 ++-- src/uu/base64/base64.md | 3 --- src/uu/base64/src/base64.rs | 4 ++-- src/uu/cat/cat.md | 3 --- src/uu/cat/src/cat.rs | 4 ++-- src/uu/cp/cp.md | 3 --- src/uu/cp/src/cp.rs | 8 ++++---- src/uu/dd/dd.md | 6 +++++- src/uu/dd/src/dd.rs | 7 ++++--- src/uu/expr/expr.md | 9 +++------ src/uu/expr/src/expr.rs | 4 ++-- src/uu/numfmt/numfmt.md | 3 --- src/uu/numfmt/src/numfmt.rs | 5 ++--- src/uu/stat/src/stat.rs | 6 ++++-- src/uu/stat/stat.md | 7 ++----- 16 files changed, 32 insertions(+), 47 deletions(-) diff --git a/src/uu/base32/base32.md b/src/uu/base32/base32.md index 0889292ce..e55fb25f6 100644 --- a/src/uu/base32/base32.md +++ b/src/uu/base32/base32.md @@ -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. diff --git a/src/uu/base32/src/base32.rs b/src/uu/base32/src/base32.rs index 01932e675..740e2d70b 100644 --- a/src/uu/base32/src/base32.rs +++ b/src/uu/base32/src/base32.rs @@ -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] diff --git a/src/uu/base64/base64.md b/src/uu/base64/base64.md index 89b9cb618..6ff657457 100644 --- a/src/uu/base64/base64.md +++ b/src/uu/base64/base64.md @@ -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. diff --git a/src/uu/base64/src/base64.rs b/src/uu/base64/src/base64.rs index 932a1f0c7..e502482e3 100644 --- a/src/uu/base64/src/base64.rs +++ b/src/uu/base64/src/base64.rs @@ -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] diff --git a/src/uu/cat/cat.md b/src/uu/cat/cat.md index 0188be123..efcd317eb 100644 --- a/src/uu/cat/cat.md +++ b/src/uu/cat/cat.md @@ -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. diff --git a/src/uu/cat/src/cat.rs b/src/uu/cat/src/cat.rs index d8cc46035..85a626d5b 100644 --- a/src/uu/cat/src/cat.rs +++ b/src/uu/cat/src/cat.rs @@ -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 { diff --git a/src/uu/cp/cp.md b/src/uu/cp/cp.md index 3659423ff..5f3cabc18 100644 --- a/src/uu/cp/cp.md +++ b/src/uu/cp/cp.md @@ -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. diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index a41ef836b..f0dc762be 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -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"; diff --git a/src/uu/dd/dd.md b/src/uu/dd/dd.md index b5e16a1bc..7f0acd718 100644 --- a/src/uu/dd/dd.md +++ b/src/uu/dd/dd.md @@ -1,7 +1,11 @@ # dd -## About +``` +dd [OPERAND]... +dd OPTION +``` + Copy, and optionally convert, a file system resource ## After Help diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 813d60ceb..51b656582 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -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..)) diff --git a/src/uu/expr/expr.md b/src/uu/expr/expr.md index 6c881eb35..eaa748076 100644 --- a/src/uu/expr/expr.md +++ b/src/uu/expr/expr.md @@ -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 \ No newline at end of file + - `EXPR_DEBUG_AST=1`: dump expression represented abstract syntax tree diff --git a/src/uu/expr/src/expr.rs b/src/uu/expr/src/expr.rs index a5fab19f2..97cfa32f3 100644 --- a/src/uu/expr/src/expr.rs +++ b/src/uu/expr/src/expr.rs @@ -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) diff --git a/src/uu/numfmt/numfmt.md b/src/uu/numfmt/numfmt.md index 3216d6eb7..74af2b016 100644 --- a/src/uu/numfmt/numfmt.md +++ b/src/uu/numfmt/numfmt.md @@ -1,13 +1,10 @@ # numfmt -## Usage ``` numfmt [OPTION]... [NUMBER]... ``` -## About - Convert numbers from/to human-readable strings ## After Help diff --git a/src/uu/numfmt/src/numfmt.rs b/src/uu/numfmt/src/numfmt.rs index 8730c9f8d..f1fd4b115 100644 --- a/src/uu/numfmt/src/numfmt.rs +++ b/src/uu/numfmt/src/numfmt.rs @@ -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"); diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index 2d0949223..fbbed8c21 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -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"); diff --git a/src/uu/stat/stat.md b/src/uu/stat/stat.md index 4304e4b0c..9228ba9dc 100644 --- a/src/uu/stat/stat.md +++ b/src/uu/stat/stat.md @@ -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`):