From 847046f3deabe69d0111b531e89f89a2ceca21ba Mon Sep 17 00:00:00 2001 From: Tilak Patidar Date: Sun, 30 Dec 2018 12:08:30 +0530 Subject: [PATCH] pr: add +page and -column --- src/pr/Cargo.toml | 1 + src/pr/pr.rs | 71 +++++++++++++++++++++++++++++++++++++++++------ tests/test_pr.rs | 17 ++++++++++++ 3 files changed, 81 insertions(+), 8 deletions(-) diff --git a/src/pr/Cargo.toml b/src/pr/Cargo.toml index d33681b5f..481404b3a 100644 --- a/src/pr/Cargo.toml +++ b/src/pr/Cargo.toml @@ -14,6 +14,7 @@ time = "0.1.40" chrono = "0.4.6" quick-error = "1.2.2" itertools = "0.7.8" +regex = "1.0.1" [dependencies.uucore] path = "../uucore" diff --git a/src/pr/pr.rs b/src/pr/pr.rs index 87aadf80c..297d9637d 100644 --- a/src/pr/pr.rs +++ b/src/pr/pr.rs @@ -13,6 +13,7 @@ extern crate quick_error; extern crate chrono; extern crate getopts; extern crate itertools; +extern crate regex; extern crate uucore; use chrono::offset::Local; @@ -22,6 +23,7 @@ use getopts::{Matches, Options}; use itertools::structs::KMergeBy; use itertools::{GroupBy, Itertools}; use quick_error::ResultExt; +use regex::Regex; use std::convert::From; use std::fs::{metadata, File, Metadata}; use std::io::{stderr, stdin, stdout, BufRead, BufReader, Lines, Read, Stdin, Stdout, Write}; @@ -322,7 +324,11 @@ pub fn uumain(args: Vec) -> i32 { opts.optflag("", "help", "display this help and exit"); opts.optflag("V", "version", "output version information and exit"); - let matches = match opts.parse(&args[1..]) { + // Remove -column option as getopts cannot parse things like -3 etc + let re = Regex::new(r"^-\d+").unwrap(); + let opt_args: Vec<&String> = args.iter().filter(|i| !re.is_match(i)).collect(); + + let matches = match opts.parse(&opt_args[1..]) { Ok(m) => m, Err(e) => panic!("Invalid options\n{}", e), }; @@ -332,7 +338,14 @@ pub fn uumain(args: Vec) -> i32 { return 0; } - let mut files: Vec = matches.free.clone(); + let mut files: Vec = matches + .free + .clone() + .iter() + .filter(|i| !i.starts_with('+') && !i.starts_with('-')) + .map(|i| i.to_string()) + .collect(); + // -n value is optional if -n is given the opts gets confused // if -n is used just before file path it might be captured as value of -n if matches.opt_str(NUMBERING_MODE_OPTION).is_some() { @@ -360,7 +373,8 @@ pub fn uumain(args: Vec) -> i32 { }; for file_group in file_groups { - let result_options: Result = build_options(&matches, &file_group); + let result_options: Result = + build_options(&matches, &file_group, args.join(" ")); if result_options.is_err() { print_error(&matches, result_options.err().unwrap()); return 1; @@ -427,6 +441,11 @@ fn print_usage(opts: &mut Options, matches: &Matches) -> i32 { 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 --{}", + COLUMN_OPTION + ); if matches.free.is_empty() { return 1; } @@ -447,7 +466,11 @@ fn parse_usize(matches: &Matches, opt: &str) -> Option> { .map(from_parse_error_to_pr_error) } -fn build_options(matches: &Matches, paths: &Vec) -> Result { +fn build_options( + matches: &Matches, + paths: &Vec, + free_args: String, +) -> Result { let invalid_pages_map = |i: String| { let unparsed_value: String = matches.opt_str(PAGE_RANGE_OPTION).unwrap(); i.parse::().map_err(|_e| { @@ -547,6 +570,19 @@ fn build_options(matches: &Matches, paths: &Vec) -> Result().map_err(|_e| { + PrError::EncounteredErrors(format!("invalid {} argument '{}'", "+", unparsed_num)) + }) + }) { + Some(res) => res?, + _ => 1, + }; + let start_page: usize = match matches .opt_str(PAGE_RANGE_OPTION) .map(|i| { @@ -556,7 +592,7 @@ fn build_options(matches: &Matches, paths: &Vec) -> Result res?, - _ => 1, + _ => start_page_in_plus_option, }; let end_page: Option = match matches @@ -608,9 +644,28 @@ fn build_options(matches: &Matches, paths: &Vec) -> Result = match parse_usize(matches, COLUMN_OPTION) { - Some(res) => Some(ColumnModeOptions { - columns: res?, + let re_col = Regex::new(r"\s*-(\d+)\s*").unwrap(); + + let start_column_option: Option = match re_col.captures(&free_args).map(|i| { + let unparsed_num = i.get(1).unwrap().as_str().trim(); + unparsed_num.parse::().map_err(|_e| { + PrError::EncounteredErrors(format!("invalid {} argument '{}'", "-", unparsed_num)) + }) + }) { + Some(res) => Some(res?), + _ => None, + }; + + // --column has more priority than -column + + let column_option_value: Option = match parse_usize(matches, COLUMN_OPTION) { + Some(res) => Some(res?), + _ => start_column_option, + }; + + let column_mode_options: Option = match column_option_value { + Some(columns) => Some(ColumnModeOptions { + columns, width: column_width, column_separator, across_mode, diff --git a/tests/test_pr.rs b/tests/test_pr.rs index 00d602b55..d02b42436 100644 --- a/tests/test_pr.rs +++ b/tests/test_pr.rs @@ -257,6 +257,15 @@ fn test_with_page_range() { expected_test_file_path, vec![(&"{last_modified_time}".to_string(), &value)], ); + + new_ucmd!() + .args(&["+15", test_file_path]) + .succeeds() + .stdout_is_templated_fixture( + expected_test_file_path, + vec![(&"{last_modified_time}".to_string(), &value)], + ); + new_ucmd!() .args(&["--pages=15:17", test_file_path]) .succeeds() @@ -336,6 +345,14 @@ fn test_with_column() { expected_test_file_path, vec![(&"{last_modified_time}".to_string(), &value)], ); + + new_ucmd!() + .args(&["--pages=3:5", "-3", "-n", test_file_path]) + .succeeds() + .stdout_is_templated_fixture( + expected_test_file_path, + vec![(&"{last_modified_time}".to_string(), &value)], + ); } #[test]