From 75b3cbcfd97ba45df9bd41d6da0d8c7ef34312eb Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Wed, 23 Feb 2022 21:56:24 +0100 Subject: [PATCH] pr: move from getopts to clap fixes --- Cargo.lock | 10 - src/uu/pr/Cargo.toml | 1 - src/uu/pr/src/pr.rs | 631 ++++++++++++++++++++----------------------- 3 files changed, 298 insertions(+), 344 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd064dc76..805c1c83a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -890,15 +890,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -dependencies = [ - "unicode-width", -] - [[package]] name = "getrandom" version = "0.2.4" @@ -2905,7 +2896,6 @@ version = "0.0.12" dependencies = [ "chrono", "clap 3.0.10", - "getopts", "itertools", "quick-error", "regex", diff --git a/src/uu/pr/Cargo.toml b/src/uu/pr/Cargo.toml index a1fdae4a2..8e9029daa 100644 --- a/src/uu/pr/Cargo.toml +++ b/src/uu/pr/Cargo.toml @@ -17,7 +17,6 @@ path = "src/pr.rs" [dependencies] clap = { version = "3.0", features = ["wrap_help", "cargo"] } uucore = { version=">=0.0.7", package="uucore", path="../../uucore", features=["entries"] } -getopts = "0.2.21" chrono = "0.4.19" quick-error = "2.0.1" itertools = "0.10.0" diff --git a/src/uu/pr/src/pr.rs b/src/uu/pr/src/pr.rs index c167770c0..8ba3872f5 100644 --- a/src/uu/pr/src/pr.rs +++ b/src/uu/pr/src/pr.rs @@ -1,5 +1,3 @@ -#![crate_name = "uu_pr"] - // This file is part of the uutils coreutils package. // // For the full copyright and license information, please view the LICENSE file @@ -13,9 +11,7 @@ extern crate quick_error; use chrono::offset::Local; use chrono::DateTime; -use clap::{App, AppSettings}; -use getopts::Matches; -use getopts::{HasArg, Occur}; +use clap::{App, AppSettings, Arg, ArgMatches}; use itertools::Itertools; use quick_error::ResultExt; use regex::Regex; @@ -26,14 +22,35 @@ use std::io::{stdin, stdout, BufRead, BufReader, Lines, Read, Write}; use std::os::unix::fs::FileTypeExt; use uucore::display::Quotable; -use uucore::error::UResult; +use uucore::error::{set_exit_code, UResult}; -type IOError = std::io::Error; - -const NAME: &str = "pr"; const VERSION: &str = env!("CARGO_PKG_VERSION"); const ABOUT: &str = "Write content of given file or standard input to standard output with pagination filter"; +const AFTER_HELP: &str = + " +PAGE\n Begin output at page number page of the formatted input. + -COLUMN\n Produce multi-column output. See --column + +The pr utility is a printing and pagination filter +for text files. When multiple input files are specified, +each is read, formatted, and written to standard +output. By default, the input is separated +into 66-line pages, each with + +o A 5-line header with the page number, date, + time, and the pathname of the file. + +o A 5-line trailer consisting of blank lines. + +If standard output is associated with a terminal, +diagnostic messages are suppressed until the pr +utility has completed processing. + +When multiple column output is specified, text columns +are of equal width. By default text columns +are separated by at least one . Input lines +that do not fit into a text column are truncated. +Lines are not truncated under single column output."; const TAB: char = '\t'; const LINES_PER_PAGE: usize = 66; const LINES_PER_PAGE_FOR_FORM_FEED: usize = 63; @@ -47,25 +64,27 @@ const DEFAULT_COLUMN_SEPARATOR: &char = &TAB; const FF: u8 = 0x0C_u8; mod options { - pub const STRING_HEADER_OPTION: &str = "h"; - pub const DOUBLE_SPACE_OPTION: &str = "d"; - pub const NUMBERING_MODE_OPTION: &str = "n"; - pub const FIRST_LINE_NUMBER_OPTION: &str = "N"; - pub const PAGE_RANGE_OPTION: &str = "pages"; - pub const NO_HEADER_TRAILER_OPTION: &str = "t"; - pub const PAGE_LENGTH_OPTION: &str = "l"; - pub const SUPPRESS_PRINTING_ERROR: &str = "r"; - pub const FORM_FEED_OPTION: &str = "F"; - pub const FORM_FEED_OPTION_SMALL: &str = "f"; - pub const COLUMN_WIDTH_OPTION: &str = "w"; - pub const PAGE_WIDTH_OPTION: &str = "W"; - pub const ACROSS_OPTION: &str = "a"; - pub const COLUMN_OPTION: &str = "column"; - pub const COLUMN_CHAR_SEPARATOR_OPTION: &str = "s"; - pub const COLUMN_STRING_SEPARATOR_OPTION: &str = "S"; - pub const MERGE_FILES_PRINT: &str = "m"; - pub const OFFSET_SPACES_OPTION: &str = "o"; - pub const JOIN_LINES_OPTION: &str = "J"; + pub const HEADER: &str = "header"; + pub const DOUBLE_SPACE: &str = "double-space"; + pub const NUMBER_LINES: &str = "number-lines"; + pub const FIRST_LINE_NUMBER: &str = "first-line-number"; + pub const PAGES: &str = "pages"; + pub const OMIT_HEADER: &str = "omit-header"; + pub const PAGE_LENGTH: &str = "length"; + pub const NO_FILE_WARNINGS: &str = "no-file-warnings"; + pub const FORM_FEED: &str = "form-feed"; + pub const COLUMN_WIDTH: &str = "width"; + pub const PAGE_WIDTH: &str = "page-width"; + pub const ACROSS: &str = "across"; + pub const COLUMN: &str = "column"; + pub const COLUMN_CHAR_SEPARATOR: &str = "separator"; + pub const COLUMN_STRING_SEPARATOR: &str = "sep-string"; + pub const MERGE: &str = "merge"; + pub const INDENT: &str = "indent"; + pub const JOIN_LINES: &str = "join-lines"; + pub const HELP: &str = "help"; + pub const VERSION: &str = "version"; + pub const FILES: &str = "files"; } struct OutputOptions { @@ -95,7 +114,7 @@ struct FileLine { line_number: usize, page_number: usize, group_key: usize, - line_content: Result, + line_content: Result, form_feeds_after: usize, } @@ -136,8 +155,8 @@ impl Default for FileLine { } } -impl From for PrError { - fn from(err: IOError) -> Self { +impl From for PrError { + fn from(err: std::io::Error) -> Self { Self::EncounteredErrors(err.to_string()) } } @@ -145,8 +164,8 @@ impl From for PrError { quick_error! { #[derive(Debug)] enum PrError { - Input(err: IOError, path: String) { - context(path: &'a str, err: IOError) -> (err, path.to_owned()) + Input(err: std::io::Error, path: String) { + context(path: &'a str, err: std::io::Error) -> (err, path.to_owned()) display("pr: Reading from input {0} gave error", path) source(err) } @@ -177,7 +196,182 @@ pub fn uu_app<'a>() -> App<'a> { App::new(uucore::util_name()) .version(VERSION) .about(ABOUT) + .after_help(AFTER_HELP) .setting(AppSettings::InferLongArgs) + .setting(AppSettings::AllArgsOverrideSelf) + .setting(AppSettings::NoAutoHelp) + .setting(AppSettings::NoAutoVersion) + .arg( + Arg::new(options::PAGES) + .long(options::PAGES) + .help("Begin and stop printing with page FIRST_PAGE[:LAST_PAGE]") + .takes_value(true) + .value_name("FIRST_PAGE[:LAST_PAGE]"), + ) + .arg( + Arg::new(options::HEADER) + .short('h') + .long(options::HEADER) + .help( + "Use the string header to replace the file name \ + in the header line.", + ) + .takes_value(true) + .value_name("STRING"), + ) + .arg( + Arg::new(options::DOUBLE_SPACE) + .short('d') + .long(options::DOUBLE_SPACE) + .help("Produce output that is double spaced. An extra character is output following every + found in the input.") + ) + .arg( + Arg::new(options::NUMBER_LINES) + .short('n') + .long(options::NUMBER_LINES) + .help("Provide width digit line numbering. The default for width, if not specified, is 5. The number occupies + the first width column positions of each text column or each line of -m output. If char (any non-digit + character) is given, it is appended to the line number to separate it from whatever follows. The default + for char is a . Line numbers longer than width columns are truncated.") + .takes_value(true) + .value_name("[char][width]") + ) + .arg( + Arg::new(options::FIRST_LINE_NUMBER) + .short('N') + .long(options::FIRST_LINE_NUMBER) + .help("start counting with NUMBER at 1st line of first page printed") + .takes_value(true) + .value_name("NUMBER") + ) + .arg( + Arg::new(options::OMIT_HEADER) + .short('t') + .long(options::OMIT_HEADER) + .help("Write neither the five-line identifying header nor the five-line trailer usually supplied for each page. Quit + writing after the last line of each file without spacing to the end of the page.") + ) + .arg( + Arg::new(options::PAGE_LENGTH) + .short('l') + .long(options::PAGE_LENGTH) + .help("Override the 66-line default (default number of lines of text 56, and with -F 63) and reset the page length to lines. If lines is not greater than the sum of both + the header and trailer depths (in lines), the pr utility shall suppress both the header and trailer, as if the + -t option were in effect. ") + .takes_value(true) + .value_name("PAGE_LENGTH") + ) + .arg( + Arg::new(options::NO_FILE_WARNINGS) + .short('r') + .long(options::NO_FILE_WARNINGS) + .help("omit warning when a file cannot be opened") + ) + .arg( + Arg::new(options::FORM_FEED) + .short('F') + .short_alias('f') + .long(options::FORM_FEED) + .help("Use a for new pages, instead of the default behavior that uses a sequence of s.") + ) + .arg( + Arg::new(options::COLUMN_WIDTH) + .short('w') + .long(options::COLUMN_WIDTH) + .help("Set the width of the line to width column positions for multiple text-column output only. If the -w option is + not specified and the -s option is not specified, the default width shall be 72. If the -w option is not specified + and the -s option is specified, the default width shall be 512.") + .takes_value(true) + .value_name("width") + ) + .arg( + Arg::new(options::PAGE_WIDTH) + .short('W') + .long(options::PAGE_WIDTH) + .help("set page width to PAGE_WIDTH (72) characters always, + truncate lines, except -J option is set, no interference + with -S or -s") + .takes_value(true) + .value_name("width") + ) + .arg( + Arg::new(options::ACROSS) + .short('a') + .long(options::ACROSS) + .help("Modify the effect of the - column option so that the columns are filled across the page in a round-robin order + (for example, when column is 2, the first input line heads column 1, the second heads column 2, the third is the + second line in column 1, and so on).") + ) + .arg( + Arg::new(options::COLUMN) + .long(options::COLUMN) + .help("Produce multi-column output that is arranged in column columns (the default shall be 1) and is written down each + column in the order in which the text is received from the input file. This option should not be used with -m. + The options -e and -i shall be assumed for multiple text-column output. Whether or not text columns are produced + with identical vertical lengths is unspecified, but a text column shall never exceed the length of the + page (see the -l option). When used with -t, use the minimum number of lines to write the output.") + .takes_value(true) + .value_name("column") + ) + .arg( + Arg::new(options::COLUMN_CHAR_SEPARATOR) + .short('s') + .long(options::COLUMN_CHAR_SEPARATOR) + .help("Separate text columns by the single character char instead of by the appropriate number of s + (default for char is the character).") + .takes_value(true) + .value_name("char") + ) + .arg( + Arg::new(options::COLUMN_STRING_SEPARATOR) + .short('S') + .long(options::COLUMN_STRING_SEPARATOR) + .help("separate columns by STRING, + without -S: Default separator with -J and + otherwise (same as -S\" \"), no effect on column options") + .takes_value(true) + .value_name("string") + ) + .arg( + Arg::new(options::MERGE) + .short('m') + .long(options::MERGE) + .help("Merge files. Standard output shall be formatted so the pr utility writes one line from each file specified by a + file operand, side by side into text columns of equal fixed widths, in terms of the number of column positions. + Implementations shall support merging of at least nine file operands.") + ) + .arg( + Arg::new(options::INDENT) + .short('o') + .long(options::INDENT) + .help("Each line of output shall be preceded by offset s. If the -o option is not specified, the default offset + shall be zero. The space taken is in addition to the output line width (see the -w option below).") + .takes_value(true) + .value_name("margin") + ) + .arg( + Arg::new(options::JOIN_LINES) + .short('J') + .help("merge full lines, turns off -W line truncation, no column + alignment, --sep-string[=STRING] sets separators") + ) + .arg( + Arg::new(options::HELP) + .long(options::HELP) + .help("Show this help message") + ) + .arg( + Arg::new(options::VERSION) + .short('V') + .long(options::VERSION) + .help("Show version information") + ) + .arg( + Arg::new(options::FILES) + .multiple_occurrences(true) + .multiple_values(true) + ) } #[uucore::main] @@ -185,229 +379,39 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let args = args .collect_str(uucore::InvalidEncodingHandling::Ignore) .accept_any(); - let mut opts = getopts::Options::new(); - - opts.opt( - "", - options::PAGE_RANGE_OPTION, - "Begin and stop printing with page FIRST_PAGE[:LAST_PAGE]", - "FIRST_PAGE[:LAST_PAGE]", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::STRING_HEADER_OPTION, - "header", - "Use the string header to replace the file name \ - in the header line.", - "STRING", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::DOUBLE_SPACE_OPTION, - "double-space", - "Produce output that is double spaced. An extra character is output following every - found in the input.", - "", - HasArg::No, - Occur::Optional, - ); - - opts.opt( - options::NUMBERING_MODE_OPTION, - "number-lines", - "Provide width digit line numbering. The default for width, if not specified, is 5. The number occupies - the first width column positions of each text column or each line of -m output. If char (any non-digit - character) is given, it is appended to the line number to separate it from whatever follows. The default - for char is a . Line numbers longer than width columns are truncated.", - "[char][width]", - HasArg::Maybe, - Occur::Optional, - ); - - opts.opt( - options::FIRST_LINE_NUMBER_OPTION, - "first-line-number", - "start counting with NUMBER at 1st line of first page printed", - "NUMBER", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::NO_HEADER_TRAILER_OPTION, - "omit-header", - "Write neither the five-line identifying header nor the five-line trailer usually supplied for each page. Quit - writing after the last line of each file without spacing to the end of the page.", - "", - HasArg::No, - Occur::Optional, - ); - - opts.opt( - options::PAGE_LENGTH_OPTION, - "length", - "Override the 66-line default (default number of lines of text 56, and with -F 63) and reset the page length to lines. If lines is not greater than the sum of both - the header and trailer depths (in lines), the pr utility shall suppress both the header and trailer, as if the - -t option were in effect. ", - "lines", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::SUPPRESS_PRINTING_ERROR, - "no-file-warnings", - "omit warning when a file cannot be opened", - "", - HasArg::No, - Occur::Optional, - ); - - opts.opt( - options::FORM_FEED_OPTION, - "form-feed", - "Use a for new pages, instead of the default behavior that uses a sequence of s.", - "", - HasArg::No, - Occur::Optional, - ); - opts.opt( - options::FORM_FEED_OPTION_SMALL, - "form-feed", - "Same as -F but pause before beginning the first page if standard output is a - terminal.", - "", - HasArg::No, - Occur::Optional, - ); - - opts.opt( - "", - options::COLUMN_OPTION, - "Produce multi-column output that is arranged in column columns (the default shall be 1) and is written down each - column in the order in which the text is received from the input file. This option should not be used with -m. - The options -e and -i shall be assumed for multiple text-column output. Whether or not text columns are produced - with identical vertical lengths is unspecified, but a text column shall never exceed the length of the - page (see the -l option). When used with -t, use the minimum number of lines to write the output.", - "[column]", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::COLUMN_WIDTH_OPTION, - "width", - "Set the width of the line to width column positions for multiple text-column output only. If the -w option is - not specified and the -s option is not specified, the default width shall be 72. If the -w option is not specified - and the -s option is specified, the default width shall be 512.", - "[width]", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::PAGE_WIDTH_OPTION, - "page-width", - "set page width to PAGE_WIDTH (72) characters always, - truncate lines, except -J option is set, no interference - with -S or -s", - "[width]", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::ACROSS_OPTION, - "across", - "Modify the effect of the - column option so that the columns are filled across the page in a round-robin order - (for example, when column is 2, the first input line heads column 1, the second heads column 2, the third is the - second line in column 1, and so on).", - "", - HasArg::No, - Occur::Optional, - ); - - opts.opt( - options::COLUMN_CHAR_SEPARATOR_OPTION, - "separator", - "Separate text columns by the single character char instead of by the appropriate number of s - (default for char is the character).", - "char", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::COLUMN_STRING_SEPARATOR_OPTION, - "sep-string", - "separate columns by STRING, - without -S: Default separator with -J and - otherwise (same as -S\" \"), no effect on column options", - "string", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::MERGE_FILES_PRINT, - "merge", - "Merge files. Standard output shall be formatted so the pr utility writes one line from each file specified by a - file operand, side by side into text columns of equal fixed widths, in terms of the number of column positions. - Implementations shall support merging of at least nine file operands.", - "", - HasArg::No, - Occur::Optional, - ); - - opts.opt( - options::OFFSET_SPACES_OPTION, - "indent", - "Each line of output shall be preceded by offset s. If the -o option is not specified, the default offset - shall be zero. The space taken is in addition to the output line width (see the -w option below).", - "offset", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::JOIN_LINES_OPTION, - "join-lines", - "merge full lines, turns off -W line truncation, no column - alignment, --sep-string[=STRING] sets separators", - "offset", - HasArg::No, - Occur::Optional, - ); - - opts.optflag("", "help", "display this help and exit"); - opts.optflag("V", "version", "output version information and exit"); let opt_args = recreate_arguments(&args); - let matches = match opts.parse(&opt_args[1..]) { + let mut app = uu_app(); + let matches = match app.try_get_matches_from_mut(opt_args) { Ok(m) => m, - Err(e) => panic!("Invalid options\n{}", e), + Err(e) => { + e.print()?; + set_exit_code(1); + return Ok(()); + } }; - if matches.opt_present("version") { - println!("{} {}", NAME, VERSION); + if matches.is_present(options::VERSION) { + println!("{}", app.render_long_version()); return Ok(()); } - let mut files = matches.free.clone(); + if matches.is_present(options::HELP) { + app.print_long_help()?; + return Ok(()); + } + + let mut files = matches + .values_of(options::FILES) + .map(|v| v.collect::>()) + .unwrap_or_default() + .clone(); if files.is_empty() { - files.insert(0, FILE_STDIN.to_owned()); + files.insert(0, FILE_STDIN); } - if matches.opt_present("help") { - return print_usage(&mut opts, &matches); - } - - let file_groups: Vec<_> = if matches.opt_present(options::MERGE_FILES_PRINT) { + let file_groups: Vec<_> = if matches.is_present(options::MERGE) { vec![files] } else { files.into_iter().map(|i| vec![i]).collect() @@ -470,56 +474,13 @@ fn recreate_arguments(args: &[String]) -> Vec { .collect() } -fn print_error(matches: &Matches, err: &PrError) { - if !matches.opt_present(options::SUPPRESS_PRINTING_ERROR) { +fn print_error(matches: &ArgMatches, err: &PrError) { + if !matches.is_present(options::NO_FILE_WARNINGS) { eprintln!("{}", err); } } -fn print_usage(opts: &mut getopts::Options, matches: &Matches) -> UResult<()> { - println!("{} {} -- print files", NAME, VERSION); - println!(); - println!( - "Usage: {} [+page] [-column] [-adFfmprt] [[-e] [char] [gap]] - [-L locale] [-h header] [[-i] [char] [gap]] - [-l lines] [-o offset] [[-s] [char]] [[-n] [char] - [width]] [-w width] [-] [file ...].", - NAME - ); - println!(); - let usage: &str = "The pr utility is a printing and pagination filter - for text files. When multiple input files are specified, - each is read, formatted, and written to standard - output. By default, the input is separated - into 66-line pages, each with - - o A 5-line header with the page number, date, - time, and the pathname of the file. - - o A 5-line trailer consisting of blank lines. - - If standard output is associated with a terminal, - diagnostic messages are suppressed until the pr - utility has completed processing. - - When multiple column output is specified, text columns - are of equal width. By default text columns - are separated by at least one . Input lines - that do not fit into a text column are truncated. - Lines are not truncated under single column output."; - println!("{}", opts.usage(usage)); - println!(" +page \t\tBegin output at page number page of the formatted input."); - println!( - " -column \t\tProduce multi-column output. Refer --{}", - options::COLUMN_OPTION - ); - if matches.free.is_empty() { - return Err(1.into()); - } - Ok(()) -} - -fn parse_usize(matches: &Matches, opt: &str) -> Option> { +fn parse_usize(matches: &ArgMatches, opt: &str) -> Option> { let from_parse_error_to_pr_error = |value_to_parse: (String, String)| { let i = value_to_parse.0; let option = value_to_parse.1; @@ -528,51 +489,51 @@ fn parse_usize(matches: &Matches, opt: &str) -> Option> { }) }; matches - .opt_str(opt) - .map(|i| (i, format!("-{}", opt))) + .value_of(opt) + .map(|i| (i.to_string(), format!("-{}", opt))) .map(from_parse_error_to_pr_error) } fn build_options( - matches: &Matches, - paths: &[String], + matches: &ArgMatches, + paths: &[&str], free_args: &str, ) -> Result { - let form_feed_used = matches.opt_present(options::FORM_FEED_OPTION) - || matches.opt_present(options::FORM_FEED_OPTION_SMALL); + let form_feed_used = matches.is_present(options::FORM_FEED); - let is_merge_mode = matches.opt_present(options::MERGE_FILES_PRINT); + let is_merge_mode = matches.is_present(options::MERGE); - if is_merge_mode && matches.opt_present(options::COLUMN_OPTION) { + if is_merge_mode && matches.is_present(options::COLUMN) { let err_msg = String::from("cannot specify number of columns when printing in parallel"); return Err(PrError::EncounteredErrors(err_msg)); } - if is_merge_mode && matches.opt_present(options::ACROSS_OPTION) { + if is_merge_mode && matches.is_present(options::ACROSS) { let err_msg = String::from("cannot specify both printing across and printing in parallel"); return Err(PrError::EncounteredErrors(err_msg)); } - let merge_files_print = if matches.opt_present(options::MERGE_FILES_PRINT) { + let merge_files_print = if matches.is_present(options::MERGE) { Some(paths.len()) } else { None }; - let header = matches.opt_str(options::STRING_HEADER_OPTION).unwrap_or( - if is_merge_mode || paths[0] == FILE_STDIN { - String::new() + let header = matches + .value_of(options::HEADER) + .unwrap_or(if is_merge_mode || paths[0] == FILE_STDIN { + "" } else { - paths[0].to_string() - }, - ); + paths[0] + }) + .to_string(); let default_first_number = NumberingMode::default().first_number; - let first_number = parse_usize(matches, options::FIRST_LINE_NUMBER_OPTION) - .unwrap_or(Ok(default_first_number))?; + let first_number = + parse_usize(matches, options::FIRST_LINE_NUMBER).unwrap_or(Ok(default_first_number))?; let number = matches - .opt_str(options::NUMBERING_MODE_OPTION) + .value_of(options::NUMBER_LINES) .map(|i| { let parse_result = i.parse::(); @@ -596,14 +557,14 @@ fn build_options( } }) .or_else(|| { - if matches.opt_present(options::NUMBERING_MODE_OPTION) { + if matches.is_present(options::NUMBER_LINES) { Some(NumberingMode::default()) } else { None } }); - let double_space = matches.opt_present(options::DOUBLE_SPACE_OPTION); + let double_space = matches.is_present(options::DOUBLE_SPACE); let content_line_separator = if double_space { "\n".repeat(2) @@ -652,7 +613,7 @@ fn build_options( }; let invalid_pages_map = |i: String| { - let unparsed_value = matches.opt_str(options::PAGE_RANGE_OPTION).unwrap(); + let unparsed_value = matches.value_of(options::PAGES).unwrap(); i.parse::().map_err(|_e| { PrError::EncounteredErrors(format!( "invalid --pages argument {}", @@ -662,7 +623,7 @@ fn build_options( }; let start_page = match matches - .opt_str(options::PAGE_RANGE_OPTION) + .value_of(options::PAGES) .map(|i| { let x: Vec<_> = i.split(':').collect(); x[0].to_string() @@ -674,7 +635,7 @@ fn build_options( }; let end_page = match matches - .opt_str(options::PAGE_RANGE_OPTION) + .value_of(options::PAGES) .filter(|i| i.contains(':')) .map(|i| { let x: Vec<_> = i.split(':').collect(); @@ -702,12 +663,12 @@ fn build_options( }; let page_length = - parse_usize(matches, options::PAGE_LENGTH_OPTION).unwrap_or(Ok(default_lines_per_page))?; + parse_usize(matches, options::PAGE_LENGTH).unwrap_or(Ok(default_lines_per_page))?; let page_length_le_ht = page_length < (HEADER_LINES_PER_PAGE + TRAILER_LINES_PER_PAGE); let display_header_and_trailer = - !(page_length_le_ht) && !matches.opt_present(options::NO_HEADER_TRAILER_OPTION); + !(page_length_le_ht) && !matches.is_present(options::OMIT_HEADER); let content_lines_per_page = if page_length_le_ht { page_length @@ -715,23 +676,24 @@ fn build_options( page_length - (HEADER_LINES_PER_PAGE + TRAILER_LINES_PER_PAGE) }; - let page_separator_char = if matches.opt_present(options::FORM_FEED_OPTION) { + let page_separator_char = if matches.is_present(options::FORM_FEED) { let bytes = vec![FF]; String::from_utf8(bytes).unwrap() } else { "\n".to_string() }; - let across_mode = matches.opt_present(options::ACROSS_OPTION); + let across_mode = matches.is_present(options::ACROSS); - let column_separator = match matches.opt_str(options::COLUMN_STRING_SEPARATOR_OPTION) { + let column_separator = match matches.value_of(options::COLUMN_STRING_SEPARATOR) { Some(x) => Some(x), - None => matches.opt_str(options::COLUMN_CHAR_SEPARATOR_OPTION), + None => matches.value_of(options::COLUMN_CHAR_SEPARATOR), } + .map(ToString::to_string) .unwrap_or_else(|| DEFAULT_COLUMN_SEPARATOR.to_string()); - let default_column_width = if matches.opt_present(options::COLUMN_WIDTH_OPTION) - && matches.opt_present(options::COLUMN_CHAR_SEPARATOR_OPTION) + let default_column_width = if matches.is_present(options::COLUMN_WIDTH) + && matches.is_present(options::COLUMN_CHAR_SEPARATOR) { DEFAULT_COLUMN_WIDTH_WITH_S_OPTION } else { @@ -739,12 +701,12 @@ fn build_options( }; let column_width = - parse_usize(matches, options::COLUMN_WIDTH_OPTION).unwrap_or(Ok(default_column_width))?; + parse_usize(matches, options::COLUMN_WIDTH).unwrap_or(Ok(default_column_width))?; - let page_width = if matches.opt_present(options::JOIN_LINES_OPTION) { + let page_width = if matches.is_present(options::JOIN_LINES) { None } else { - match parse_usize(matches, options::PAGE_WIDTH_OPTION) { + match parse_usize(matches, options::PAGE_WIDTH) { Some(res) => Some(res?), None => None, } @@ -764,7 +726,7 @@ fn build_options( // --column has more priority than -column - let column_option_value = match parse_usize(matches, options::COLUMN_OPTION) { + let column_option_value = match parse_usize(matches, options::COLUMN) { Some(res) => Some(res?), None => start_column_option, }; @@ -776,9 +738,8 @@ fn build_options( across_mode, }); - let offset_spaces = - " ".repeat(parse_usize(matches, options::OFFSET_SPACES_OPTION).unwrap_or(Ok(0))?); - let join_lines = matches.opt_present(options::JOIN_LINES_OPTION); + let offset_spaces = " ".repeat(parse_usize(matches, options::INDENT).unwrap_or(Ok(0))?); + let join_lines = matches.is_present(options::JOIN_LINES); let col_sep_for_printing = column_mode_options .as_ref() @@ -855,7 +816,7 @@ fn open(path: &str) -> Result, PrError> { .unwrap_or_else(|_| Err(PrError::NotExists(path.to_string()))) } -fn split_lines_if_form_feed(file_content: Result) -> Vec { +fn split_lines_if_form_feed(file_content: Result) -> Vec { file_content .map(|content| { let mut lines = Vec::new(); @@ -972,7 +933,7 @@ fn read_stream_and_create_pages( ) } -fn mpr(paths: &[String], options: &OutputOptions) -> Result { +fn mpr(paths: &[&str], options: &OutputOptions) -> Result { let n_files = paths.len(); // Check if files exists @@ -1032,7 +993,11 @@ fn mpr(paths: &[String], options: &OutputOptions) -> Result { Ok(0) } -fn print_page(lines: &[FileLine], options: &OutputOptions, page: usize) -> Result { +fn print_page( + lines: &[FileLine], + options: &OutputOptions, + page: usize, +) -> Result { let line_separator = options.line_separator.as_bytes(); let page_separator = options.page_separator_char.as_bytes(); @@ -1064,7 +1029,7 @@ fn write_columns( lines: &[FileLine], options: &OutputOptions, out: &mut impl Write, -) -> Result { +) -> Result { let line_separator = options.content_line_separator.as_bytes(); let content_lines_per_page = if options.double_space {