Skip to content

Commit

Permalink
wip ~ refactor + improve docs * REVISE wc.rs based on prior changes
Browse files Browse the repository at this point in the history
  • Loading branch information
rivy committed Jun 19, 2023
1 parent 4bfc1df commit 2ccc524
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 16 deletions.
26 changes: 17 additions & 9 deletions src/uucore/src/lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,29 +98,37 @@ use once_cell::sync::Lazy;
// }
// ```

// ToDO: [2023-05; rivy] plan to rename `bin!()` macro to `main!()` macro
// #... requires renaming the uucore_procs `main` macro to `uumain` (with corresponding uutil code file updates)
// #... then updating all utilities to use new macro name in the *main.rs* code files
// ToDO: [2023-05; rivy] plan to rename `bin!()` macro to `util_bin!()` macro
// #... requires updating all utilities to use new macro name in the *main.rs* code files
// ToDO: [2023-05; rivy] improve docs
// ToDO: [2023-05; rivy] add additional overload for `main!()` which uses ` env!("CARGO_PKG_NAME")` to generate the $util string (allows the simple use of `uucore::main!()`)
// ToDO: [2023-05; rivy] add additional overload for `main!()` which uses `env!("CARGO_PKG_NAME")` to generate the $util string (allows the simple use of `uucore::main!()`)
// ... ! not possible due to macro hygiene/sand-boxing
// ... alternatively using a proc macro with env!("CARGO_PKG_NAME") as an argument would work except `env!()` expansion is not eager

// bin!()
/// Generate the standard boilerplate main function for a utility.
/// `bin!(UTIL_CRATE_NAME)`
///
/// This macro should be used in the *src/main.rs* file of the utility.
/// `UTIL_CRATE_NAME` ~ name of the utility crate as a *bare word*
///
/// Generate the standard boilerplate main function for the specified utility.
///
/// Requires the utility crate to contain a function with the signature `fn _uumain_adapter_(args: impl uucore::Args) -> i32 { ... }`.
/// This function is usually generated by the `#[uucore::main]` attribute/decorator macro.
///
/// This macro should be used as the content of the designated *bin* file for the utility (usually as *src/main.rs*).
///
/// ### Example
/// ```rust
/// // * uu/arch/src/main.rs
/// uucore::bin!(uu_arch);
/// ```
// note: this could be rewritten as a procedural macro, removing the need for the `$util_crate:ident` parameter if/when eager expansion of `env!("CARGO_PKG_NAME")` is possible.
#[macro_export]
macro_rules! bin {
($util:ident) => {
($util_crate_name:ident) => {
pub fn main() {
use std::io::Write;
uucore::panic::mute_sigpipe_panic(); // suppress extraneous error output for SIGPIPE failures/panics
let code: i32 = $util::_uumain_adapter_(uucore::args_os()); // execute wrapped utility main fn (expects proc_macro generated `_uumain_adapter_(args: impl uucore::Args) -> i32`)
let code: i32 = $util_crate_name::_uumain_adapter_(uucore::args_os()); // execute utility main fn via adapter (expects proc_macro generated `_uumain_adapter_(args: impl uucore::Args) -> i32`)
std::io::stdout().flush().expect("could not flush stdout"); // (defensively) flush stdout for utility prior to exit; see <https://github.com/rust-lang/rust/issues/23818>
std::process::exit(code);
}
Expand Down
54 changes: 47 additions & 7 deletions src/uucore_procs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,50 @@ use quote::quote;
// ToDO: [2023-05; rivy] plan to rename `main` proc macro attribute to `uumain`; improve docs
// #... requires subsequent updating of all utilities to use the renamed macro attribute for their `uumain(...)`

// util_bin!()
/// Designate the utility "main" fn (for CLI), generating a wrapper target for the "true" `fn main(...)`,
/// which is itself generated by the `uucore::bin!()` macro.
///
/// The decorated function must be have the signature `fn uumain(args: impl uucore::Args) -> UResult<()> { ... }`.
/// By convention, `uumain` is used as the name of the utility main function (though the actual
/// name is just a placeholder and irrelevant).
///
/// ### Example
/// ```rust
/// #[uucore::main]
/// pub fn uumain(args: impl uucore::Args) -> UResult<()> {
/// //...
/// }
/// ```
#[proc_macro]
pub fn uutil_bin_proc(input: TokenStream) -> TokenStream {
let lit_str: syn::LitStr = syn::parse_macro_input!(input);
let s = lit_str.value();
// eprintln!("s: {}", s);
// let stream = proc_macro2::TokenStream::from(input);
// eprintln!("stream: {}", stream);
// let s = env!("CARGO_PKG_NAME");
// eprintln!("CARGO_PKG_NAME: {}", s);
// let ast: syn::ItemFn = syn::parse2(stream.clone()).unwrap();
// eprintln!("ast: {}", ast);
// // let s: syn::LitStr = syn::parse2(stream.clone()).expect("Expected a string literal");
// // let s: syn::LitStr = syn::parse(input).expect("Expected a string literal");
let new = quote!(
pub fn main() {
use std::io::Write;
uucore::panic::mute_sigpipe_panic(); // suppress extraneous error output for SIGPIPE failures/panics
println!("{}", #s);
let code: i32 = #s::_uumain_adapter_(uucore::args_os()); // execute wrapped utility main fn (expects proc_macro generated `_uumain_adapter_(args: impl uucore::Args) -> i32`)
std::io::stdout().flush().expect("could not flush stdout"); // (defensively) flush stdout for utility prior to exit; see <https://github.com/rust-lang/rust/issues/23818>
std::process::exit(code);
}
);
TokenStream::from(new)
// TokenStream::new()
}

// main!()
/// Designate the utility "main" fn, generating a wrapper target for the "true" `fn main(...)`,
/// Decorate/designate the utility "main" fn (for CLI), generating an target adapter fn for the "true" `fn main(...)`,
/// which is itself generated by the `uucore::bin!()` macro.
///
/// The decorated function must be have the signature `fn uumain(args: impl uucore::Args) -> UResult<()> { ... }`.
Expand All @@ -29,22 +71,20 @@ use quote::quote;
/// }
/// ```
#[proc_macro_attribute]
pub fn main(_args: TokenStream, input: TokenStream) -> TokenStream {
pub fn main(_attrs: TokenStream, input: TokenStream) -> TokenStream {
let stream = proc_macro2::TokenStream::from(input);
let ast: syn::ItemFn = syn::parse2(stream.clone()).unwrap();
let original_fn_name = &ast.sig.ident;

let new = quote!(
#stream
pub fn _uumain_adapter_(args: impl uucore::Args) -> i32 {
#stream
let result: UResult<()> = #original_fn_name(args);
eprintln!("result={:?}", result);
// eprintln!("result={:?}", result);
match result {
Ok(()) => uucore::error::get_exit_code(),
Err(e) => {
// FixME: [2022-04; terts] (HACK!) for color output, `clap` error text is directly displayed on stdout/stderr when "formatted"
// ! ... (see `impl Display for ClapErrorWrapper` in `uucore/src/error.rs`)
// clap messages (error, help, version) can have extra trailing newlines, so we trim them
// errors with an exit code of 0 (eg, Help/Usage "errors" from `clap`) are displayed without extra adornment
let s = format!("{}", e);
let code = e.code();
if s != "" {
Expand Down

0 comments on commit 2ccc524

Please sign in to comment.