mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 20:17:45 +00:00
pr: refactor and fmt fill_missing_lines and error checks
pr: Remove unwanted brancing in fill_missing_lines pr: Remove unnecessary error check pr: Rename key to group_key in FileLine pr: Reformat test_pr with rustfmt
This commit is contained in:
parent
5956894d00
commit
5c6c524334
2 changed files with 528 additions and 430 deletions
546
src/pr/pr.rs
546
src/pr/pr.rs
|
@ -10,35 +10,36 @@
|
||||||
extern crate unix_socket;
|
extern crate unix_socket;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate quick_error;
|
extern crate quick_error;
|
||||||
extern crate itertools;
|
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
extern crate getopts;
|
extern crate getopts;
|
||||||
|
extern crate itertools;
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
use std::io::{BufRead, BufReader, stdin, stdout, stderr, Error, Read, Write, Stdout, Lines, Stdin};
|
|
||||||
use std::vec::Vec;
|
|
||||||
use chrono::offset::Local;
|
use chrono::offset::Local;
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
|
use getopts::{HasArg, Occur};
|
||||||
use getopts::{Matches, Options};
|
use getopts::{Matches, Options};
|
||||||
use std::fs::{metadata, File, Metadata};
|
use itertools::structs::KMergeBy;
|
||||||
#[cfg(unix)]
|
use itertools::{GroupBy, Itertools};
|
||||||
use std::os::unix::fs::FileTypeExt;
|
|
||||||
use quick_error::ResultExt;
|
use quick_error::ResultExt;
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use getopts::{HasArg, Occur};
|
use std::fs::{metadata, File, Metadata};
|
||||||
|
use std::io::{stderr, stdin, stdout, BufRead, BufReader, Lines, Read, Stdin, Stdout, Write};
|
||||||
|
use std::iter::{Enumerate, Map, SkipWhile, TakeWhile};
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
use itertools::{Itertools, GroupBy};
|
#[cfg(unix)]
|
||||||
use std::iter::{Enumerate, Map, TakeWhile, SkipWhile};
|
use std::os::unix::fs::FileTypeExt;
|
||||||
use itertools::structs::KMergeBy;
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
type IOError = std::io::Error;
|
||||||
|
|
||||||
static NAME: &str = "pr";
|
static NAME: &str = "pr";
|
||||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
static TAB: char = '\t';
|
static TAB: char = '\t';
|
||||||
|
static NEW_LINE: &str = "\n";
|
||||||
static LINES_PER_PAGE: usize = 66;
|
static LINES_PER_PAGE: usize = 66;
|
||||||
static HEADER_LINES_PER_PAGE: usize = 5;
|
static HEADER_LINES_PER_PAGE: usize = 5;
|
||||||
static TRAILER_LINES_PER_PAGE: usize = 5;
|
static TRAILER_LINES_PER_PAGE: usize = 5;
|
||||||
static NUMBERING_MODE_DEFAULT_SEPARATOR: &str = "\t";
|
|
||||||
static NUMBERING_MODE_DEFAULT_WIDTH: usize = 5;
|
|
||||||
static STRING_HEADER_OPTION: &str = "h";
|
static STRING_HEADER_OPTION: &str = "h";
|
||||||
static DOUBLE_SPACE_OPTION: &str = "d";
|
static DOUBLE_SPACE_OPTION: &str = "d";
|
||||||
static NUMBERING_MODE_OPTION: &str = "n";
|
static NUMBERING_MODE_OPTION: &str = "n";
|
||||||
|
@ -57,7 +58,8 @@ static OFFSET_SPACES_OPTION: &str = "o";
|
||||||
static FILE_STDIN: &str = "-";
|
static FILE_STDIN: &str = "-";
|
||||||
static READ_BUFFER_SIZE: usize = 1024 * 64;
|
static READ_BUFFER_SIZE: usize = 1024 * 64;
|
||||||
static DEFAULT_COLUMN_WIDTH: usize = 72;
|
static DEFAULT_COLUMN_WIDTH: usize = 72;
|
||||||
static DEFAULT_COLUMN_SEPARATOR: &str = "\t";
|
static DEFAULT_COLUMN_SEPARATOR: &char = &TAB;
|
||||||
|
static BLANK_STRING: &str = "";
|
||||||
|
|
||||||
struct OutputOptions {
|
struct OutputOptions {
|
||||||
/// Line numbering mode
|
/// Line numbering mode
|
||||||
|
@ -67,7 +69,7 @@ struct OutputOptions {
|
||||||
line_separator: String,
|
line_separator: String,
|
||||||
content_line_separator: String,
|
content_line_separator: String,
|
||||||
last_modified_time: String,
|
last_modified_time: String,
|
||||||
start_page: Option<usize>,
|
start_page: usize,
|
||||||
end_page: Option<usize>,
|
end_page: Option<usize>,
|
||||||
display_header: bool,
|
display_header: bool,
|
||||||
display_trailer: bool,
|
display_trailer: bool,
|
||||||
|
@ -75,15 +77,15 @@ struct OutputOptions {
|
||||||
page_separator_char: String,
|
page_separator_char: String,
|
||||||
column_mode_options: Option<ColumnModeOptions>,
|
column_mode_options: Option<ColumnModeOptions>,
|
||||||
merge_files_print: Option<usize>,
|
merge_files_print: Option<usize>,
|
||||||
offset_spaces: usize
|
offset_spaces: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FileLine {
|
struct FileLine {
|
||||||
file_id: usize,
|
file_id: usize,
|
||||||
line_number: usize,
|
line_number: usize,
|
||||||
page_number: usize,
|
page_number: usize,
|
||||||
key: usize,
|
group_key: usize,
|
||||||
line_content: Result<String, Error>,
|
line_content: Result<String, IOError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<FileLine> for FileLine {
|
impl AsRef<FileLine> for FileLine {
|
||||||
|
@ -93,7 +95,7 @@ impl AsRef<FileLine> for FileLine {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ColumnModeOptions {
|
struct ColumnModeOptions {
|
||||||
width: Option<usize>,
|
width: usize,
|
||||||
columns: usize,
|
columns: usize,
|
||||||
column_separator: String,
|
column_separator: String,
|
||||||
across_mode: bool,
|
across_mode: bool,
|
||||||
|
@ -115,21 +117,15 @@ struct NumberingMode {
|
||||||
impl Default for NumberingMode {
|
impl Default for NumberingMode {
|
||||||
fn default() -> NumberingMode {
|
fn default() -> NumberingMode {
|
||||||
NumberingMode {
|
NumberingMode {
|
||||||
width: NUMBERING_MODE_DEFAULT_WIDTH,
|
width: 5,
|
||||||
separator: NUMBERING_MODE_DEFAULT_SEPARATOR.to_string(),
|
separator: TAB.to_string(),
|
||||||
first_number: 1,
|
first_number: 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Error> for PrError {
|
impl From<IOError> for PrError {
|
||||||
fn from(err: Error) -> Self {
|
fn from(err: IOError) -> Self {
|
||||||
PrError::EncounteredErrors(err.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::num::ParseIntError> for PrError {
|
|
||||||
fn from(err: std::num::ParseIntError) -> Self {
|
|
||||||
PrError::EncounteredErrors(err.to_string())
|
PrError::EncounteredErrors(err.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,8 +133,8 @@ impl From<std::num::ParseIntError> for PrError {
|
||||||
quick_error! {
|
quick_error! {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum PrError {
|
enum PrError {
|
||||||
Input(err: Error, path: String) {
|
Input(err: IOError, path: String) {
|
||||||
context(path: &'a str, err: Error) -> (err, path.to_owned())
|
context(path: &'a str, err: IOError) -> (err, path.to_owned())
|
||||||
display("pr: Reading from input {0} gave error", path)
|
display("pr: Reading from input {0} gave error", path)
|
||||||
cause(err)
|
cause(err)
|
||||||
}
|
}
|
||||||
|
@ -353,7 +349,6 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
files.insert(0, FILE_STDIN.to_owned());
|
files.insert(0, FILE_STDIN.to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if matches.opt_present("help") {
|
if matches.opt_present("help") {
|
||||||
return print_usage(&mut opts, &matches);
|
return print_usage(&mut opts, &matches);
|
||||||
}
|
}
|
||||||
|
@ -381,7 +376,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
print_error(&matches, error);
|
print_error(&matches, error);
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
_ => 0
|
_ => 0,
|
||||||
};
|
};
|
||||||
if status != 0 {
|
if status != 0 {
|
||||||
return status;
|
return status;
|
||||||
|
@ -403,10 +398,13 @@ fn print_error(matches: &Matches, err: PrError) {
|
||||||
fn print_usage(opts: &mut Options, matches: &Matches) -> i32 {
|
fn print_usage(opts: &mut Options, matches: &Matches) -> i32 {
|
||||||
println!("{} {} -- print files", NAME, VERSION);
|
println!("{} {} -- print files", NAME, VERSION);
|
||||||
println!();
|
println!();
|
||||||
println!("Usage: {} [+page] [-column] [-adFfmprt] [[-e] [char] [gap]]
|
println!(
|
||||||
|
"Usage: {} [+page] [-column] [-adFfmprt] [[-e] [char] [gap]]
|
||||||
[-L locale] [-h header] [[-i] [char] [gap]]
|
[-L locale] [-h header] [[-i] [char] [gap]]
|
||||||
[-l lines] [-o offset] [[-s] [char]] [[-n] [char]
|
[-l lines] [-o offset] [[-s] [char]] [[-n] [char]
|
||||||
[width]] [-w width] [-] [file ...].", NAME);
|
[width]] [-w width] [-] [file ...].",
|
||||||
|
NAME
|
||||||
|
);
|
||||||
println!();
|
println!();
|
||||||
let usage: &str = "The pr utility is a printing and pagination filter
|
let usage: &str = "The pr utility is a printing and pagination filter
|
||||||
for text files. When multiple input files are spec-
|
for text files. When multiple input files are spec-
|
||||||
|
@ -435,16 +433,39 @@ fn print_usage(opts: &mut Options, matches: &Matches) -> i32 {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_usize(matches: &Matches, opt: &str) -> Option<Result<usize, PrError>> {
|
||||||
|
let from_parse_error_to_pr_error = |value_to_parse: (String, String)| {
|
||||||
|
let i = value_to_parse.0;
|
||||||
|
let option = value_to_parse.1;
|
||||||
|
i.parse::<usize>().map_err(|_e| {
|
||||||
|
PrError::EncounteredErrors(format!("invalid {} argument '{}'", option, i))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
matches
|
||||||
|
.opt_str(opt)
|
||||||
|
.map(|i| (i, format!("-{}", opt)))
|
||||||
|
.map(from_parse_error_to_pr_error)
|
||||||
|
}
|
||||||
|
|
||||||
fn build_options(matches: &Matches, paths: &Vec<String>) -> Result<OutputOptions, PrError> {
|
fn build_options(matches: &Matches, paths: &Vec<String>) -> Result<OutputOptions, PrError> {
|
||||||
|
let invalid_pages_map = |i: String| {
|
||||||
|
let unparsed_value: String = matches.opt_str(PAGE_RANGE_OPTION).unwrap();
|
||||||
|
i.parse::<usize>().map_err(|_e| {
|
||||||
|
PrError::EncounteredErrors(format!("invalid --pages argument '{}'", unparsed_value))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
let is_merge_mode: bool = matches.opt_present(MERGE_FILES_PRINT);
|
let is_merge_mode: bool = matches.opt_present(MERGE_FILES_PRINT);
|
||||||
|
|
||||||
if is_merge_mode && matches.opt_present(COLUMN_OPTION) {
|
if is_merge_mode && matches.opt_present(COLUMN_OPTION) {
|
||||||
let err_msg: String = "cannot specify number of columns when printing in parallel".to_string();
|
let err_msg: String =
|
||||||
|
"cannot specify number of columns when printing in parallel".to_string();
|
||||||
return Err(PrError::EncounteredErrors(err_msg));
|
return Err(PrError::EncounteredErrors(err_msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_merge_mode && matches.opt_present(ACROSS_OPTION) {
|
if is_merge_mode && matches.opt_present(ACROSS_OPTION) {
|
||||||
let err_msg: String = "cannot specify both printing across and printing in parallel".to_string();
|
let err_msg: String =
|
||||||
|
"cannot specify both printing across and printing in parallel".to_string();
|
||||||
return Err(PrError::EncounteredErrors(err_msg));
|
return Err(PrError::EncounteredErrors(err_msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,7 +475,9 @@ fn build_options(matches: &Matches, paths: &Vec<String>) -> Result<OutputOptions
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let header: String = matches.opt_str(STRING_HEADER_OPTION).unwrap_or(if is_merge_mode {
|
let header: String = matches
|
||||||
|
.opt_str(STRING_HEADER_OPTION)
|
||||||
|
.unwrap_or(if is_merge_mode {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
} else {
|
} else {
|
||||||
if paths[0].to_string() == FILE_STDIN {
|
if paths[0].to_string() == FILE_STDIN {
|
||||||
|
@ -465,11 +488,12 @@ fn build_options(matches: &Matches, paths: &Vec<String>) -> Result<OutputOptions
|
||||||
});
|
});
|
||||||
|
|
||||||
let default_first_number: usize = NumberingMode::default().first_number;
|
let default_first_number: usize = NumberingMode::default().first_number;
|
||||||
let first_number: usize = matches.opt_str(FIRST_LINE_NUMBER_OPTION).map(|n| {
|
let first_number: usize =
|
||||||
n.parse::<usize>().unwrap_or(default_first_number)
|
parse_usize(matches, FIRST_LINE_NUMBER_OPTION).unwrap_or(Ok(default_first_number))?;
|
||||||
}).unwrap_or(default_first_number);
|
|
||||||
|
|
||||||
let numbering_options: Option<NumberingMode> = matches.opt_str(NUMBERING_MODE_OPTION).map(|i| {
|
let numbering_options: Option<NumberingMode> = matches
|
||||||
|
.opt_str(NUMBERING_MODE_OPTION)
|
||||||
|
.map(|i| {
|
||||||
let parse_result: Result<usize, ParseIntError> = i.parse::<usize>();
|
let parse_result: Result<usize, ParseIntError> = i.parse::<usize>();
|
||||||
|
|
||||||
let separator: String = if parse_result.is_err() {
|
let separator: String = if parse_result.is_err() {
|
||||||
|
@ -486,7 +510,9 @@ fn build_options(matches: &Matches, paths: &Vec<String>) -> Result<OutputOptions
|
||||||
if is_a_file(&i) {
|
if is_a_file(&i) {
|
||||||
NumberingMode::default().width
|
NumberingMode::default().width
|
||||||
} else {
|
} else {
|
||||||
i[1..].parse::<usize>().unwrap_or(NumberingMode::default().width)
|
i[1..]
|
||||||
|
.parse::<usize>()
|
||||||
|
.unwrap_or(NumberingMode::default().width)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
parse_result.unwrap()
|
parse_result.unwrap()
|
||||||
|
@ -497,7 +523,8 @@ fn build_options(matches: &Matches, paths: &Vec<String>) -> Result<OutputOptions
|
||||||
separator,
|
separator,
|
||||||
first_number,
|
first_number,
|
||||||
}
|
}
|
||||||
}).or_else(|| {
|
})
|
||||||
|
.or_else(|| {
|
||||||
if matches.opt_present(NUMBERING_MODE_OPTION) {
|
if matches.opt_present(NUMBERING_MODE_OPTION) {
|
||||||
return Some(NumberingMode::default());
|
return Some(NumberingMode::default());
|
||||||
}
|
}
|
||||||
|
@ -507,12 +534,12 @@ fn build_options(matches: &Matches, paths: &Vec<String>) -> Result<OutputOptions
|
||||||
let double_space: bool = matches.opt_present(DOUBLE_SPACE_OPTION);
|
let double_space: bool = matches.opt_present(DOUBLE_SPACE_OPTION);
|
||||||
|
|
||||||
let content_line_separator: String = if double_space {
|
let content_line_separator: String = if double_space {
|
||||||
"\n\n".to_string()
|
NEW_LINE.repeat(2)
|
||||||
} else {
|
} else {
|
||||||
"\n".to_string()
|
NEW_LINE.to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
let line_separator: String = "\n".to_string();
|
let line_separator: String = NEW_LINE.to_string();
|
||||||
|
|
||||||
let last_modified_time: String = if is_merge_mode || paths[0].eq(FILE_STDIN) {
|
let last_modified_time: String = if is_merge_mode || paths[0].eq(FILE_STDIN) {
|
||||||
current_time()
|
current_time()
|
||||||
|
@ -520,48 +547,46 @@ fn build_options(matches: &Matches, paths: &Vec<String>) -> Result<OutputOptions
|
||||||
file_last_modified_time(paths.get(0).unwrap())
|
file_last_modified_time(paths.get(0).unwrap())
|
||||||
};
|
};
|
||||||
|
|
||||||
let invalid_pages_map = |i: Result<usize, ParseIntError>| {
|
let start_page: usize = match matches
|
||||||
let unparsed_value: String = matches.opt_str(PAGE_RANGE_OPTION).unwrap();
|
.opt_str(PAGE_RANGE_OPTION)
|
||||||
match i {
|
.map(|i| {
|
||||||
Ok(val) => Ok(val),
|
|
||||||
Err(_e) => Err(PrError::EncounteredErrors(format!("invalid --pages argument '{}'", unparsed_value)))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let start_page: Option<usize> = match matches.opt_str(PAGE_RANGE_OPTION).map(|i| {
|
|
||||||
let x: Vec<&str> = i.split(":").collect();
|
let x: Vec<&str> = i.split(":").collect();
|
||||||
x[0].parse::<usize>()
|
x[0].to_string()
|
||||||
}).map(invalid_pages_map)
|
})
|
||||||
|
.map(invalid_pages_map)
|
||||||
{
|
{
|
||||||
Some(res) => Some(res?),
|
Some(res) => res?,
|
||||||
_ => None
|
_ => 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let end_page: Option<usize> = match matches.opt_str(PAGE_RANGE_OPTION)
|
let end_page: Option<usize> = match matches
|
||||||
|
.opt_str(PAGE_RANGE_OPTION)
|
||||||
.filter(|i: &String| i.contains(":"))
|
.filter(|i: &String| i.contains(":"))
|
||||||
.map(|i: String| {
|
.map(|i: String| {
|
||||||
let x: Vec<&str> = i.split(":").collect();
|
let x: Vec<&str> = i.split(":").collect();
|
||||||
x[1].parse::<usize>()
|
x[1].to_string()
|
||||||
})
|
})
|
||||||
.map(invalid_pages_map)
|
.map(invalid_pages_map)
|
||||||
{
|
{
|
||||||
Some(res) => Some(res?),
|
Some(res) => Some(res?),
|
||||||
_ => None
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if start_page.is_some() && end_page.is_some() && start_page.unwrap() > end_page.unwrap() {
|
if end_page.is_some() && start_page > end_page.unwrap() {
|
||||||
return Err(PrError::EncounteredErrors(format!("invalid --pages argument '{}:{}'", start_page.unwrap(), end_page.unwrap())));
|
return Err(PrError::EncounteredErrors(format!(
|
||||||
|
"invalid --pages argument '{}:{}'",
|
||||||
|
start_page,
|
||||||
|
end_page.unwrap()
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let page_length: usize = match matches.opt_str(PAGE_LENGTH_OPTION).map(|i| {
|
let page_length: usize =
|
||||||
i.parse::<usize>()
|
parse_usize(matches, PAGE_LENGTH_OPTION).unwrap_or(Ok(LINES_PER_PAGE))?;
|
||||||
}) {
|
|
||||||
Some(res) => res?,
|
|
||||||
_ => LINES_PER_PAGE
|
|
||||||
};
|
|
||||||
let page_length_le_ht: bool = page_length < (HEADER_LINES_PER_PAGE + TRAILER_LINES_PER_PAGE);
|
let page_length_le_ht: bool = page_length < (HEADER_LINES_PER_PAGE + TRAILER_LINES_PER_PAGE);
|
||||||
|
|
||||||
let display_header_and_trailer: bool = !(page_length_le_ht) && !matches.opt_present(NO_HEADER_TRAILER_OPTION);
|
let display_header_and_trailer: bool =
|
||||||
|
!(page_length_le_ht) && !matches.opt_present(NO_HEADER_TRAILER_OPTION);
|
||||||
|
|
||||||
let content_lines_per_page: usize = if page_length_le_ht {
|
let content_lines_per_page: usize = if page_length_le_ht {
|
||||||
page_length
|
page_length
|
||||||
|
@ -569,45 +594,31 @@ fn build_options(matches: &Matches, paths: &Vec<String>) -> Result<OutputOptions
|
||||||
page_length - (HEADER_LINES_PER_PAGE + TRAILER_LINES_PER_PAGE)
|
page_length - (HEADER_LINES_PER_PAGE + TRAILER_LINES_PER_PAGE)
|
||||||
};
|
};
|
||||||
|
|
||||||
let page_separator_char: String = matches.opt_str(FORM_FEED_OPTION).map(|_i| {
|
let page_separator_char: String = matches
|
||||||
'\u{000A}'.to_string()
|
.opt_str(FORM_FEED_OPTION)
|
||||||
}).unwrap_or("\n".to_string());
|
.map(|_i| '\u{000A}'.to_string())
|
||||||
|
.unwrap_or(NEW_LINE.to_string());
|
||||||
|
|
||||||
let column_width: Option<usize> = match matches.opt_str(COLUMN_WIDTH_OPTION).map(|i| i.parse::<usize>()) {
|
let column_width: usize =
|
||||||
Some(res) => Some(res?),
|
parse_usize(matches, COLUMN_WIDTH_OPTION).unwrap_or(Ok(DEFAULT_COLUMN_WIDTH))?;
|
||||||
_ => None
|
|
||||||
};
|
|
||||||
|
|
||||||
let across_mode: bool = matches.opt_present(ACROSS_OPTION);
|
let across_mode: bool = matches.opt_present(ACROSS_OPTION);
|
||||||
|
|
||||||
let column_separator: String = matches.opt_str(COLUMN_SEPARATOR_OPTION)
|
let column_separator: String = matches
|
||||||
|
.opt_str(COLUMN_SEPARATOR_OPTION)
|
||||||
.unwrap_or(DEFAULT_COLUMN_SEPARATOR.to_string());
|
.unwrap_or(DEFAULT_COLUMN_SEPARATOR.to_string());
|
||||||
|
|
||||||
|
let column_mode_options: Option<ColumnModeOptions> = match parse_usize(matches, COLUMN_OPTION) {
|
||||||
let column_mode_options: Option<ColumnModeOptions> = match matches.opt_str(COLUMN_OPTION).map(|i| {
|
Some(res) => Some(ColumnModeOptions {
|
||||||
i.parse::<usize>()
|
|
||||||
}) {
|
|
||||||
Some(res) => {
|
|
||||||
Some(ColumnModeOptions {
|
|
||||||
columns: res?,
|
columns: res?,
|
||||||
width: match column_width {
|
width: column_width,
|
||||||
Some(x) => Some(x),
|
|
||||||
None => Some(DEFAULT_COLUMN_WIDTH)
|
|
||||||
},
|
|
||||||
column_separator,
|
column_separator,
|
||||||
across_mode,
|
across_mode,
|
||||||
})
|
}),
|
||||||
}
|
_ => None,
|
||||||
_ => None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let offset_spaces: usize = matches.opt_str(OFFSET_SPACES_OPTION)
|
let offset_spaces: usize = parse_usize(matches, OFFSET_SPACES_OPTION).unwrap_or(Ok(0))?;
|
||||||
.map(|i| {
|
|
||||||
match i.parse::<usize>() {
|
|
||||||
Ok(val)=> Ok(val),
|
|
||||||
Err(e)=> Err(PrError::EncounteredErrors("".to_string()))
|
|
||||||
}
|
|
||||||
}).unwrap_or(Ok(0))?;
|
|
||||||
|
|
||||||
Ok(OutputOptions {
|
Ok(OutputOptions {
|
||||||
number: numbering_options,
|
number: numbering_options,
|
||||||
|
@ -634,93 +645,80 @@ fn open(path: &str) -> Result<Box<Read>, PrError> {
|
||||||
return Ok(Box::new(stdin) as Box<Read>);
|
return Ok(Box::new(stdin) as Box<Read>);
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata(path).map(|i: Metadata| {
|
metadata(path)
|
||||||
|
.map(|i: Metadata| {
|
||||||
let path_string = path.to_string();
|
let path_string = path.to_string();
|
||||||
match i.file_type() {
|
match i.file_type() {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
ft if ft.is_block_device() =>
|
ft if ft.is_block_device() => Err(PrError::UnknownFiletype(path_string)),
|
||||||
{
|
|
||||||
Err(PrError::UnknownFiletype(path_string))
|
|
||||||
}
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
ft if ft.is_char_device() =>
|
ft if ft.is_char_device() => Err(PrError::UnknownFiletype(path_string)),
|
||||||
{
|
|
||||||
Err(PrError::UnknownFiletype(path_string))
|
|
||||||
}
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
ft if ft.is_fifo() =>
|
ft if ft.is_fifo() => Err(PrError::UnknownFiletype(path_string)),
|
||||||
{
|
|
||||||
Err(PrError::UnknownFiletype(path_string))
|
|
||||||
}
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
ft if ft.is_socket() =>
|
ft if ft.is_socket() => Err(PrError::IsSocket(path_string)),
|
||||||
{
|
|
||||||
Err(PrError::IsSocket(path_string))
|
|
||||||
}
|
|
||||||
ft if ft.is_dir() => Err(PrError::IsDirectory(path_string)),
|
ft if ft.is_dir() => Err(PrError::IsDirectory(path_string)),
|
||||||
ft if ft.is_file() || ft.is_symlink() => Ok(Box::new(File::open(path).context(path)?) as Box<Read>),
|
ft if ft.is_file() || ft.is_symlink() => {
|
||||||
_ => Err(PrError::UnknownFiletype(path_string))
|
Ok(Box::new(File::open(path).context(path)?) as Box<Read>)
|
||||||
}
|
}
|
||||||
}).unwrap_or(Err(PrError::NotExists(path.to_string())))
|
_ => Err(PrError::UnknownFiletype(path_string)),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(Err(PrError::NotExists(path.to_string())))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pr(path: &String, options: &OutputOptions) -> Result<i32, PrError> {
|
fn pr(path: &String, options: &OutputOptions) -> Result<i32, PrError> {
|
||||||
let start_page: &usize = options.start_page.as_ref().unwrap_or(&1);
|
let start_page: &usize = &options.start_page;
|
||||||
let start_line_number: usize = get_start_line_number(options);
|
let start_line_number: usize = get_start_line_number(options);
|
||||||
let last_page: Option<&usize> = options.end_page.as_ref();
|
let last_page: Option<&usize> = options.end_page.as_ref();
|
||||||
let lines_needed_per_page: usize = lines_to_read_for_page(options);
|
let lines_needed_per_page: usize = lines_to_read_for_page(options);
|
||||||
let file_line_groups: GroupBy<usize, Map<TakeWhile<SkipWhile<Map<Enumerate<Lines<BufReader<Box<Read>>>>, _>, _>, _>, _>, _> =
|
let start_line_index_of_start_page = (start_page - 1) * lines_needed_per_page;
|
||||||
BufReader::with_capacity(READ_BUFFER_SIZE, open(path).unwrap())
|
let file_line_groups: GroupBy<
|
||||||
|
usize,
|
||||||
|
Map<TakeWhile<SkipWhile<Map<Enumerate<Lines<BufReader<Box<Read>>>>, _>, _>, _>, _>,
|
||||||
|
_,
|
||||||
|
> = BufReader::with_capacity(READ_BUFFER_SIZE, open(path).unwrap())
|
||||||
.lines()
|
.lines()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(move |i: (usize, Result<String, Error>)| {
|
.map(|i: (usize, Result<String, IOError>)| FileLine {
|
||||||
FileLine {
|
|
||||||
file_id: 0,
|
file_id: 0,
|
||||||
line_number: i.0,
|
line_number: i.0,
|
||||||
line_content: i.1,
|
line_content: i.1,
|
||||||
page_number: 0,
|
page_number: 0,
|
||||||
key: 0,
|
group_key: 0,
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.skip_while(move |file_line: &FileLine| {
|
.skip_while(|file_line: &FileLine| {
|
||||||
// Skip the initial lines if not in page range
|
// Skip the initial lines if not in page range
|
||||||
let start_line_index_of_start_page = (start_page - 1) * lines_needed_per_page;
|
|
||||||
file_line.line_number < (start_line_index_of_start_page)
|
file_line.line_number < (start_line_index_of_start_page)
|
||||||
})
|
})
|
||||||
.take_while(move |file_line: &FileLine| {
|
.take_while(|file_line: &FileLine| {
|
||||||
// Only read the file until provided last page reached
|
// Only read the file until provided last page reached
|
||||||
last_page
|
last_page
|
||||||
.map(|lp| file_line.line_number < ((*lp) * lines_needed_per_page))
|
.map(|lp| file_line.line_number < ((*lp) * lines_needed_per_page))
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
})
|
})
|
||||||
.map(move |file_line: FileLine| {
|
.map(|file_line: FileLine| {
|
||||||
let page_number = ((file_line.line_number + 1) as f64 / lines_needed_per_page as f64).ceil() as usize;
|
let page_number =
|
||||||
|
((file_line.line_number + 1) as f64 / lines_needed_per_page as f64).ceil() as usize;
|
||||||
FileLine {
|
FileLine {
|
||||||
line_number: file_line.line_number + start_line_number,
|
line_number: file_line.line_number + start_line_number,
|
||||||
page_number,
|
page_number,
|
||||||
key: page_number,
|
group_key: page_number,
|
||||||
..file_line
|
..file_line
|
||||||
}
|
}
|
||||||
}) // get display line number with line content
|
}) // get display line number with line content
|
||||||
.group_by(|file_line: &FileLine| {
|
.group_by(|file_line: &FileLine| file_line.group_key);
|
||||||
file_line.page_number
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
for (page_number, file_line_group) in file_line_groups.into_iter() {
|
for (page_number, file_line_group) in file_line_groups.into_iter() {
|
||||||
let mut lines: Vec<FileLine> = Vec::new();
|
let mut lines: Vec<FileLine> = Vec::new();
|
||||||
for file_line in file_line_group {
|
for file_line in file_line_group {
|
||||||
if file_line.line_content.is_err() {
|
if file_line.line_content.is_err() {
|
||||||
return Err(PrError::from(file_line.line_content.unwrap_err()));
|
return Err(file_line.line_content.unwrap_err().into());
|
||||||
}
|
}
|
||||||
lines.push(file_line);
|
lines.push(file_line);
|
||||||
}
|
}
|
||||||
let print_status: Result<usize, Error> = print_page(&lines, options, &page_number);
|
print_page(&lines, options, &page_number)?;
|
||||||
if print_status.is_err() {
|
|
||||||
return Err(PrError::from(print_status.unwrap_err()));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
@ -730,10 +728,18 @@ fn mpr(paths: &Vec<String>, options: &OutputOptions) -> Result<i32, PrError> {
|
||||||
|
|
||||||
let lines_needed_per_page: usize = lines_to_read_for_page(options);
|
let lines_needed_per_page: usize = lines_to_read_for_page(options);
|
||||||
let lines_needed_per_page_f64: f64 = lines_needed_per_page as f64;
|
let lines_needed_per_page_f64: f64 = lines_needed_per_page as f64;
|
||||||
let start_page: &usize = options.start_page.as_ref().unwrap_or(&1);
|
let start_page: &usize = &options.start_page;
|
||||||
let last_page: Option<&usize> = options.end_page.as_ref();
|
let last_page: Option<&usize> = options.end_page.as_ref();
|
||||||
|
let start_line_index_of_start_page = (start_page - 1) * lines_needed_per_page;
|
||||||
|
|
||||||
let file_line_groups: GroupBy<usize, KMergeBy<Map<TakeWhile<SkipWhile<Map<Enumerate<Lines<BufReader<Box<Read>>>>, _>, _>, _>, _>, _>, _> = paths
|
let file_line_groups: GroupBy<
|
||||||
|
usize,
|
||||||
|
KMergeBy<
|
||||||
|
Map<TakeWhile<SkipWhile<Map<Enumerate<Lines<BufReader<Box<Read>>>>, _>, _>, _>, _>,
|
||||||
|
_,
|
||||||
|
>,
|
||||||
|
_,
|
||||||
|
> = paths
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|indexed_path: (usize, &String)| {
|
.map(|indexed_path: (usize, &String)| {
|
||||||
|
@ -741,63 +747,56 @@ fn mpr(paths: &Vec<String>, options: &OutputOptions) -> Result<i32, PrError> {
|
||||||
BufReader::with_capacity(READ_BUFFER_SIZE, open(indexed_path.1).unwrap())
|
BufReader::with_capacity(READ_BUFFER_SIZE, open(indexed_path.1).unwrap())
|
||||||
.lines()
|
.lines()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(move |i: (usize, Result<String, Error>)| {
|
.map(move |i: (usize, Result<String, IOError>)| FileLine {
|
||||||
FileLine {
|
|
||||||
file_id: indexed_path.0,
|
file_id: indexed_path.0,
|
||||||
line_number: i.0,
|
line_number: i.0,
|
||||||
line_content: i.1,
|
line_content: i.1,
|
||||||
page_number: 0,
|
page_number: 0,
|
||||||
key: 0,
|
group_key: 0,
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.skip_while(move |file_line: &FileLine| {
|
.skip_while(move |file_line: &FileLine| {
|
||||||
// Skip the initial lines if not in page range
|
// Skip the initial lines if not in page range
|
||||||
let start_line_index_of_start_page = (start_page - 1) * lines_needed_per_page;
|
|
||||||
file_line.line_number < (start_line_index_of_start_page)
|
file_line.line_number < (start_line_index_of_start_page)
|
||||||
})
|
})
|
||||||
.take_while(move |file_line: &FileLine| {
|
.take_while(move |file_line: &FileLine| {
|
||||||
// Only read the file until provided last page reached
|
// Only read the file until provided last page reached
|
||||||
|
|
||||||
last_page
|
last_page
|
||||||
.map(|lp| file_line.line_number < ((*lp) * lines_needed_per_page))
|
.map(|lp| file_line.line_number < ((*lp) * lines_needed_per_page))
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
})
|
})
|
||||||
.map(move |file_line: FileLine| {
|
.map(move |file_line: FileLine| {
|
||||||
let page_number = ((file_line.line_number + 2 - start_line_number) as f64 / (lines_needed_per_page_f64)).ceil() as usize;
|
let page_number = ((file_line.line_number + 2 - start_line_number) as f64
|
||||||
|
/ (lines_needed_per_page_f64))
|
||||||
|
.ceil() as usize;
|
||||||
FileLine {
|
FileLine {
|
||||||
line_number: file_line.line_number + start_line_number,
|
line_number: file_line.line_number + start_line_number,
|
||||||
page_number,
|
page_number,
|
||||||
key: page_number * nfiles + file_line.file_id,
|
group_key: page_number * nfiles + file_line.file_id,
|
||||||
..file_line
|
..file_line
|
||||||
}
|
}
|
||||||
}) // get display line number with line content
|
}) // get display line number with line content
|
||||||
})
|
})
|
||||||
.kmerge_by(|a: &FileLine, b: &FileLine| {
|
.kmerge_by(|a: &FileLine, b: &FileLine| {
|
||||||
if a.key == b.key {
|
if a.group_key == b.group_key {
|
||||||
a.line_number < b.line_number
|
a.line_number < b.line_number
|
||||||
} else {
|
} else {
|
||||||
a.key < b.key
|
a.group_key < b.group_key
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.group_by(|file_line: &FileLine| {
|
.group_by(|file_line: &FileLine| file_line.group_key);
|
||||||
file_line.key
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut lines: Vec<FileLine> = Vec::new();
|
let mut lines: Vec<FileLine> = Vec::new();
|
||||||
let start_page: &usize = options.start_page.as_ref().unwrap_or(&1);
|
let start_page: &usize = &options.start_page;
|
||||||
let mut page_counter: usize = *start_page;
|
let mut page_counter: usize = *start_page;
|
||||||
for (_key, file_line_group) in file_line_groups.into_iter() {
|
for (_key, file_line_group) in file_line_groups.into_iter() {
|
||||||
for file_line in file_line_group {
|
for file_line in file_line_group {
|
||||||
if file_line.line_content.is_err() {
|
if file_line.line_content.is_err() {
|
||||||
return Err(PrError::from(file_line.line_content.unwrap_err()));
|
return Err(file_line.line_content.unwrap_err().into());
|
||||||
}
|
}
|
||||||
let new_page_number = file_line.page_number;
|
let new_page_number = file_line.page_number;
|
||||||
if page_counter != new_page_number {
|
if page_counter != new_page_number {
|
||||||
fill_missing_files(&mut lines, lines_needed_per_page, &nfiles, page_counter);
|
fill_missing_lines(&mut lines, lines_needed_per_page, &nfiles, page_counter);
|
||||||
let print_status: Result<usize, Error> = print_page(&lines, options, &page_counter);
|
print_page(&lines, options, &page_counter)?;
|
||||||
if print_status.is_err() {
|
|
||||||
return Err(PrError::from(print_status.unwrap_err()));
|
|
||||||
}
|
|
||||||
lines = Vec::new();
|
lines = Vec::new();
|
||||||
}
|
}
|
||||||
lines.push(file_line);
|
lines.push(file_line);
|
||||||
|
@ -805,76 +804,73 @@ fn mpr(paths: &Vec<String>, options: &OutputOptions) -> Result<i32, PrError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fill_missing_files(&mut lines, lines_needed_per_page, &nfiles, page_counter);
|
fill_missing_lines(&mut lines, lines_needed_per_page, &nfiles, page_counter);
|
||||||
let print_status: Result<usize, Error> = print_page(&lines, options, &page_counter);
|
print_page(&lines, options, &page_counter)?;
|
||||||
if print_status.is_err() {
|
|
||||||
return Err(PrError::from(print_status.unwrap_err()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_missing_files(lines: &mut Vec<FileLine>, lines_per_file: usize, nfiles: &usize, page_number: usize) {
|
fn fill_missing_lines(
|
||||||
|
lines: &mut Vec<FileLine>,
|
||||||
|
lines_per_file: usize,
|
||||||
|
nfiles: &usize,
|
||||||
|
page_number: usize,
|
||||||
|
) {
|
||||||
let init_line_number = (page_number - 1) * lines_per_file + 1;
|
let init_line_number = (page_number - 1) * lines_per_file + 1;
|
||||||
let final_line_number = page_number * lines_per_file;
|
|
||||||
let mut file_id_counter: usize = 0;
|
let mut file_id_counter: usize = 0;
|
||||||
let mut line_number_counter: usize = init_line_number;
|
let mut line_number_counter: usize = init_line_number;
|
||||||
let mut lines_processed: usize = 0;
|
let mut lines_processed_per_file: usize = 0;
|
||||||
let mut file_id_missing: bool = false;
|
|
||||||
for mut i in 0..lines_per_file * nfiles {
|
for mut i in 0..lines_per_file * nfiles {
|
||||||
let file_id = lines.get(i).map(|i: &FileLine| i.file_id).unwrap_or(file_id_counter);
|
let file_id = lines
|
||||||
|
.get(i)
|
||||||
|
.map(|i: &FileLine| i.file_id)
|
||||||
|
.unwrap_or(file_id_counter);
|
||||||
let line_number = lines.get(i).map(|i: &FileLine| i.line_number).unwrap_or(1);
|
let line_number = lines.get(i).map(|i: &FileLine| i.line_number).unwrap_or(1);
|
||||||
if lines_processed == lines_per_file {
|
if lines_processed_per_file == lines_per_file {
|
||||||
line_number_counter = init_line_number;
|
line_number_counter = init_line_number;
|
||||||
file_id_counter += 1;
|
file_id_counter += 1;
|
||||||
lines_processed = 0;
|
lines_processed_per_file = 0;
|
||||||
file_id_missing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if file_id_counter >= *nfiles {
|
|
||||||
file_id_counter = 0;
|
|
||||||
file_id_missing = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if file_id != file_id_counter {
|
if file_id != file_id_counter {
|
||||||
file_id_missing = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if file_id_missing {
|
|
||||||
// Insert missing file_ids
|
// Insert missing file_ids
|
||||||
lines.insert(i, FileLine {
|
lines.insert(
|
||||||
|
i,
|
||||||
|
FileLine {
|
||||||
file_id: file_id_counter,
|
file_id: file_id_counter,
|
||||||
line_number: line_number_counter,
|
line_number: line_number_counter,
|
||||||
line_content: Ok("".to_string()),
|
line_content: Ok("".to_string()),
|
||||||
page_number,
|
page_number,
|
||||||
key: 0,
|
group_key: 0,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
line_number_counter += 1;
|
line_number_counter += 1;
|
||||||
} else {
|
} else if line_number < line_number_counter {
|
||||||
// Insert missing lines for a file_id
|
// Insert missing lines for a file_id
|
||||||
if line_number < line_number_counter {
|
|
||||||
line_number_counter += 1;
|
line_number_counter += 1;
|
||||||
lines.insert(i, FileLine {
|
lines.insert(
|
||||||
|
i,
|
||||||
|
FileLine {
|
||||||
file_id,
|
file_id,
|
||||||
line_number: line_number_counter,
|
line_number: line_number_counter,
|
||||||
line_content: Ok("".to_string()),
|
line_content: Ok("".to_string()),
|
||||||
page_number,
|
page_number,
|
||||||
key: 0,
|
group_key: 0,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
line_number_counter = line_number;
|
line_number_counter = line_number;
|
||||||
if line_number_counter == final_line_number {
|
|
||||||
line_number_counter = init_line_number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lines_processed_per_file += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lines_processed += 1;
|
fn print_page(
|
||||||
}
|
lines: &Vec<FileLine>,
|
||||||
}
|
options: &OutputOptions,
|
||||||
|
page: &usize,
|
||||||
fn print_page(lines: &Vec<FileLine>, options: &OutputOptions, page: &usize) -> Result<usize, Error> {
|
) -> Result<usize, IOError> {
|
||||||
let page_separator = options.page_separator_char.as_bytes();
|
let page_separator = options.page_separator_char.as_bytes();
|
||||||
let header: Vec<String> = header_content(options, page);
|
let header: Vec<String> = header_content(options, page);
|
||||||
let trailer_content: Vec<String> = trailer_content(options);
|
let trailer_content: Vec<String> = trailer_content(options);
|
||||||
|
@ -902,7 +898,11 @@ fn print_page(lines: &Vec<FileLine>, options: &OutputOptions, page: &usize) -> R
|
||||||
Ok(lines_written)
|
Ok(lines_written)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_columns(lines: &Vec<FileLine>, options: &OutputOptions, out: &mut Stdout) -> Result<usize, Error> {
|
fn write_columns(
|
||||||
|
lines: &Vec<FileLine>,
|
||||||
|
options: &OutputOptions,
|
||||||
|
out: &mut Stdout,
|
||||||
|
) -> Result<usize, IOError> {
|
||||||
let line_separator = options.content_line_separator.as_bytes();
|
let line_separator = options.content_line_separator.as_bytes();
|
||||||
let content_lines_per_page = if options.double_space {
|
let content_lines_per_page = if options.double_space {
|
||||||
options.content_lines_per_page / 2
|
options.content_lines_per_page / 2
|
||||||
|
@ -910,12 +910,10 @@ fn write_columns(lines: &Vec<FileLine>, options: &OutputOptions, out: &mut Stdou
|
||||||
options.content_lines_per_page
|
options.content_lines_per_page
|
||||||
};
|
};
|
||||||
|
|
||||||
let width: usize = options
|
let width: usize = options.number.as_ref().map(|i| i.width).unwrap_or(0);
|
||||||
.number.as_ref()
|
|
||||||
.map(|i| i.width)
|
|
||||||
.unwrap_or(0);
|
|
||||||
let number_separator: String = options
|
let number_separator: String = options
|
||||||
.number.as_ref()
|
.number
|
||||||
|
.as_ref()
|
||||||
.map(|i| i.separator.to_string())
|
.map(|i| i.separator.to_string())
|
||||||
.unwrap_or(NumberingMode::default().separator);
|
.unwrap_or(NumberingMode::default().separator);
|
||||||
|
|
||||||
|
@ -923,25 +921,31 @@ fn write_columns(lines: &Vec<FileLine>, options: &OutputOptions, out: &mut Stdou
|
||||||
let columns = options.merge_files_print.unwrap_or(get_columns(options));
|
let columns = options.merge_files_print.unwrap_or(get_columns(options));
|
||||||
let def_sep = DEFAULT_COLUMN_SEPARATOR.to_string();
|
let def_sep = DEFAULT_COLUMN_SEPARATOR.to_string();
|
||||||
let col_sep: &String = options
|
let col_sep: &String = options
|
||||||
.column_mode_options.as_ref()
|
.column_mode_options
|
||||||
|
.as_ref()
|
||||||
.map(|i| &i.column_separator)
|
.map(|i| &i.column_separator)
|
||||||
.unwrap_or(options
|
.unwrap_or(
|
||||||
|
options
|
||||||
.merge_files_print
|
.merge_files_print
|
||||||
.map(|_k| &def_sep)
|
.map(|_k| &def_sep)
|
||||||
.unwrap_or(&blank_line)
|
.unwrap_or(&blank_line),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// TODO simplify
|
||||||
let col_width: Option<usize> = options
|
let col_width: Option<usize> = options
|
||||||
.column_mode_options.as_ref()
|
.column_mode_options
|
||||||
.map(|i| i.width)
|
.as_ref()
|
||||||
.unwrap_or(options
|
.map(|i| Some(i.width))
|
||||||
|
.unwrap_or(
|
||||||
|
options
|
||||||
.merge_files_print
|
.merge_files_print
|
||||||
.map(|_k| Some(DEFAULT_COLUMN_WIDTH))
|
.map(|_k| Some(DEFAULT_COLUMN_WIDTH))
|
||||||
.unwrap_or(None)
|
.unwrap_or(None),
|
||||||
);
|
);
|
||||||
|
|
||||||
let across_mode = options
|
let across_mode = options
|
||||||
.column_mode_options.as_ref()
|
.column_mode_options
|
||||||
|
.as_ref()
|
||||||
.map(|i| i.across_mode)
|
.map(|i| i.across_mode)
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
@ -951,18 +955,16 @@ fn write_columns(lines: &Vec<FileLine>, options: &OutputOptions, out: &mut Stdou
|
||||||
let is_number_mode = options.number.is_some();
|
let is_number_mode = options.number.is_some();
|
||||||
let fetch_indexes: Vec<Vec<usize>> = if across_mode {
|
let fetch_indexes: Vec<Vec<usize>> = if across_mode {
|
||||||
(0..content_lines_per_page)
|
(0..content_lines_per_page)
|
||||||
.map(|a|
|
.map(|a| (0..columns).map(|i| a * columns + i).collect())
|
||||||
(0..columns)
|
|
||||||
.map(|i| a * columns + i)
|
|
||||||
.collect()
|
.collect()
|
||||||
).collect()
|
|
||||||
} else {
|
} else {
|
||||||
(0..content_lines_per_page)
|
(0..content_lines_per_page)
|
||||||
.map(|start|
|
.map(|start| {
|
||||||
(0..columns)
|
(0..columns)
|
||||||
.map(|i| start + content_lines_per_page * i)
|
.map(|i| start + content_lines_per_page * i)
|
||||||
.collect()
|
.collect()
|
||||||
).collect()
|
})
|
||||||
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
let spaces = " ".repeat(*offset_spaces);
|
let spaces = " ".repeat(*offset_spaces);
|
||||||
|
@ -975,10 +977,20 @@ fn write_columns(lines: &Vec<FileLine>, options: &OutputOptions, out: &mut Stdou
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let file_line: &FileLine = lines.get(index).unwrap();
|
let file_line: &FileLine = lines.get(index).unwrap();
|
||||||
let trimmed_line: String = format!("{}{}", spaces, get_line_for_printing(
|
let trimmed_line: String = format!(
|
||||||
file_line, &width, &number_separator, columns, col_width,
|
"{}{}",
|
||||||
is_number_mode, &options.merge_files_print, &i,
|
spaces,
|
||||||
));
|
get_line_for_printing(
|
||||||
|
file_line,
|
||||||
|
&width,
|
||||||
|
&number_separator,
|
||||||
|
columns,
|
||||||
|
col_width,
|
||||||
|
is_number_mode,
|
||||||
|
&options.merge_files_print,
|
||||||
|
&i,
|
||||||
|
)
|
||||||
|
);
|
||||||
out.write(trimmed_line.as_bytes())?;
|
out.write(trimmed_line.as_bytes())?;
|
||||||
if (i + 1) != indexes {
|
if (i + 1) != indexes {
|
||||||
out.write(col_sep.as_bytes())?;
|
out.write(col_sep.as_bytes())?;
|
||||||
|
@ -990,30 +1002,37 @@ fn write_columns(lines: &Vec<FileLine>, options: &OutputOptions, out: &mut Stdou
|
||||||
Ok(lines_printed)
|
Ok(lines_printed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_line_for_printing(file_line: &FileLine, width: &usize,
|
fn get_line_for_printing(
|
||||||
separator: &String, columns: usize,
|
file_line: &FileLine,
|
||||||
|
width: &usize,
|
||||||
|
separator: &String,
|
||||||
|
columns: usize,
|
||||||
col_width: Option<usize>,
|
col_width: Option<usize>,
|
||||||
is_number_mode: bool, merge_files_print: &Option<usize>,
|
is_number_mode: bool,
|
||||||
|
merge_files_print: &Option<usize>,
|
||||||
index: &usize,
|
index: &usize,
|
||||||
) -> String {
|
) -> String {
|
||||||
let should_show_line_number_merge_file = merge_files_print.is_none() || index == &usize::min_value();
|
let should_show_line_number_merge_file =
|
||||||
|
merge_files_print.is_none() || index == &usize::min_value();
|
||||||
let should_show_line_number = is_number_mode && should_show_line_number_merge_file;
|
let should_show_line_number = is_number_mode && should_show_line_number_merge_file;
|
||||||
let fmtd_line_number: String = if should_show_line_number {
|
let fmtd_line_number: String = if should_show_line_number {
|
||||||
get_fmtd_line_number(&width, file_line.line_number, &separator)
|
get_fmtd_line_number(&width, file_line.line_number, &separator)
|
||||||
} else {
|
} else {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
};
|
};
|
||||||
let mut complete_line = format!("{}{}", fmtd_line_number, file_line.line_content.as_ref().unwrap());
|
let mut complete_line = format!(
|
||||||
|
"{}{}",
|
||||||
|
fmtd_line_number,
|
||||||
|
file_line.line_content.as_ref().unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
let tab_count: usize = complete_line
|
let tab_count: usize = complete_line.chars().filter(|i| i == &TAB).count();
|
||||||
.chars()
|
|
||||||
.filter(|i| i == &TAB)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
let display_length = complete_line.len() + (tab_count * 7);
|
let display_length = complete_line.len() + (tab_count * 7);
|
||||||
// TODO Adjust the width according to -n option
|
// TODO Adjust the width according to -n option
|
||||||
// TODO actual len of the string vs display len of string because of tabs
|
// TODO actual len of the string vs display len of string because of tabs
|
||||||
col_width.map(|i| {
|
col_width
|
||||||
|
.map(|i| {
|
||||||
let min_width = (i - (columns - 1)) / columns;
|
let min_width = (i - (columns - 1)) / columns;
|
||||||
if display_length < min_width {
|
if display_length < min_width {
|
||||||
for _i in 0..(min_width - display_length) {
|
for _i in 0..(min_width - display_length) {
|
||||||
|
@ -1021,27 +1040,38 @@ fn get_line_for_printing(file_line: &FileLine, width: &usize,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
complete_line
|
complete_line.chars().take(min_width).collect()
|
||||||
.chars()
|
})
|
||||||
.take(min_width)
|
.unwrap_or(complete_line)
|
||||||
.collect()
|
|
||||||
}).unwrap_or(complete_line)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_fmtd_line_number(width: &usize, line_number: usize, separator: &String) -> String {
|
fn get_fmtd_line_number(width: &usize, line_number: usize, separator: &String) -> String {
|
||||||
let line_str = line_number.to_string();
|
let line_str = line_number.to_string();
|
||||||
if line_str.len() >= *width {
|
if line_str.len() >= *width {
|
||||||
format!("{:>width$}{}", &line_str[line_str.len() - *width..], separator, width = width)
|
format!(
|
||||||
|
"{:>width$}{}",
|
||||||
|
&line_str[line_str.len() - *width..],
|
||||||
|
separator,
|
||||||
|
width = width
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
format!("{:>width$}{}", line_str, separator, width = width)
|
format!("{:>width$}{}", line_str, separator, width = width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn header_content(options: &OutputOptions, page: &usize) -> Vec<String> {
|
fn header_content(options: &OutputOptions, page: &usize) -> Vec<String> {
|
||||||
if options.display_header {
|
if options.display_header {
|
||||||
let first_line: String = format!("{} {} Page {}", options.last_modified_time, options.header, page);
|
let first_line: String = format!(
|
||||||
vec!["".to_string(), "".to_string(), first_line, "".to_string(), "".to_string()]
|
"{} {} Page {}",
|
||||||
|
options.last_modified_time, options.header, page
|
||||||
|
);
|
||||||
|
vec![
|
||||||
|
BLANK_STRING.to_string(),
|
||||||
|
BLANK_STRING.to_string(),
|
||||||
|
first_line,
|
||||||
|
BLANK_STRING.to_string(),
|
||||||
|
BLANK_STRING.to_string(),
|
||||||
|
]
|
||||||
} else {
|
} else {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
@ -1049,12 +1079,17 @@ fn header_content(options: &OutputOptions, page: &usize) -> Vec<String> {
|
||||||
|
|
||||||
fn file_last_modified_time(path: &str) -> String {
|
fn file_last_modified_time(path: &str) -> String {
|
||||||
let file_metadata = metadata(path);
|
let file_metadata = metadata(path);
|
||||||
return file_metadata.map(|i| {
|
return file_metadata
|
||||||
return i.modified().map(|x| {
|
.map(|i| {
|
||||||
|
return i
|
||||||
|
.modified()
|
||||||
|
.map(|x| {
|
||||||
let datetime: DateTime<Local> = x.into();
|
let datetime: DateTime<Local> = x.into();
|
||||||
datetime.format("%b %d %H:%M %Y").to_string()
|
datetime.format("%b %d %H:%M %Y").to_string()
|
||||||
}).unwrap_or(String::new());
|
})
|
||||||
}).unwrap_or(String::new());
|
.unwrap_or(String::new());
|
||||||
|
})
|
||||||
|
.unwrap_or(String::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn current_time() -> String {
|
fn current_time() -> String {
|
||||||
|
@ -1064,7 +1099,13 @@ fn current_time() -> String {
|
||||||
|
|
||||||
fn trailer_content(options: &OutputOptions) -> Vec<String> {
|
fn trailer_content(options: &OutputOptions) -> Vec<String> {
|
||||||
if options.as_ref().display_trailer {
|
if options.as_ref().display_trailer {
|
||||||
vec!["".to_string(), "".to_string(), "".to_string(), "".to_string(), "".to_string()]
|
vec![
|
||||||
|
BLANK_STRING.to_string(),
|
||||||
|
BLANK_STRING.to_string(),
|
||||||
|
BLANK_STRING.to_string(),
|
||||||
|
BLANK_STRING.to_string(),
|
||||||
|
BLANK_STRING.to_string(),
|
||||||
|
]
|
||||||
} else {
|
} else {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
@ -1076,10 +1117,7 @@ fn trailer_content(options: &OutputOptions) -> Vec<String> {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `opts` - A reference to OutputOptions
|
/// * `opts` - A reference to OutputOptions
|
||||||
fn get_start_line_number(opts: &OutputOptions) -> usize {
|
fn get_start_line_number(opts: &OutputOptions) -> usize {
|
||||||
opts.number
|
opts.number.as_ref().map(|i| i.first_number).unwrap_or(1)
|
||||||
.as_ref()
|
|
||||||
.map(|i| i.first_number)
|
|
||||||
.unwrap_or(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns number of lines to read from input for constructing one page of pr output.
|
/// Returns number of lines to read from input for constructing one page of pr output.
|
||||||
|
|
208
tests/test_pr.rs
208
tests/test_pr.rs
|
@ -2,25 +2,29 @@ extern crate chrono;
|
||||||
|
|
||||||
use common::util::*;
|
use common::util::*;
|
||||||
use std::fs::metadata;
|
use std::fs::metadata;
|
||||||
use test_pr::chrono::DateTime;
|
|
||||||
use test_pr::chrono::offset::Local;
|
use test_pr::chrono::offset::Local;
|
||||||
|
use test_pr::chrono::DateTime;
|
||||||
|
|
||||||
fn file_last_modified_time(ucmd: &UCommand, path: &str) -> String {
|
fn file_last_modified_time(ucmd: &UCommand, path: &str) -> String {
|
||||||
let tmp_dir_path = ucmd.get_full_fixture_path(path);
|
let tmp_dir_path = ucmd.get_full_fixture_path(path);
|
||||||
let file_metadata = metadata(tmp_dir_path);
|
let file_metadata = metadata(tmp_dir_path);
|
||||||
return file_metadata.map(|i| {
|
return file_metadata
|
||||||
return i.modified().map(|x| {
|
.map(|i| {
|
||||||
|
return i
|
||||||
|
.modified()
|
||||||
|
.map(|x| {
|
||||||
let datetime: DateTime<Local> = x.into();
|
let datetime: DateTime<Local> = x.into();
|
||||||
datetime.format("%b %d %H:%M %Y").to_string()
|
datetime.format("%b %d %H:%M %Y").to_string()
|
||||||
}).unwrap_or(String::new());
|
})
|
||||||
}).unwrap_or(String::new());
|
.unwrap_or(String::new());
|
||||||
|
})
|
||||||
|
.unwrap_or(String::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn now_time() -> String {
|
fn now_time() -> String {
|
||||||
Local::now().format("%b %d %H:%M %Y").to_string()
|
Local::now().format("%b %d %H:%M %Y").to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_without_any_options() {
|
fn test_without_any_options() {
|
||||||
let test_file_path = "test_one_page.log";
|
let test_file_path = "test_one_page.log";
|
||||||
|
@ -30,7 +34,10 @@ fn test_without_any_options() {
|
||||||
scenario
|
scenario
|
||||||
.args(&[test_file_path])
|
.args(&[test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![(&"{last_modified_time}".to_string(), &value)]);
|
.stdout_is_templated_fixture(
|
||||||
|
expected_test_file_path,
|
||||||
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -42,7 +49,10 @@ fn test_with_numbering_option() {
|
||||||
scenario
|
scenario
|
||||||
.args(&["-n", test_file_path])
|
.args(&["-n", test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![(&"{last_modified_time}".to_string(), &value)]);
|
.stdout_is_templated_fixture(
|
||||||
|
expected_test_file_path,
|
||||||
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -54,7 +64,10 @@ fn test_with_numbering_option_when_content_is_less_than_page() {
|
||||||
scenario
|
scenario
|
||||||
.args(&["-n", test_file_path])
|
.args(&["-n", test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![(&"{last_modified_time}".to_string(), &value)]);
|
.stdout_is_templated_fixture(
|
||||||
|
expected_test_file_path,
|
||||||
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -66,7 +79,10 @@ fn test_with_numbering_option_with_number_width() {
|
||||||
scenario
|
scenario
|
||||||
.args(&["-n", "2", test_file_path])
|
.args(&["-n", "2", test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![(&"{last_modified_time}".to_string(), &value)]);
|
.stdout_is_templated_fixture(
|
||||||
|
expected_test_file_path,
|
||||||
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -79,10 +95,13 @@ fn test_with_header_option() {
|
||||||
scenario
|
scenario
|
||||||
.args(&["-h", header, test_file_path])
|
.args(&["-h", header, test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
|
expected_test_file_path,
|
||||||
|
vec![
|
||||||
(&"{last_modified_time}".to_string(), &value),
|
(&"{last_modified_time}".to_string(), &value),
|
||||||
(&"{header}".to_string(), &header.to_string())
|
(&"{header}".to_string(), &header.to_string()),
|
||||||
]);
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -95,10 +114,13 @@ fn test_with_long_header_option() {
|
||||||
scenario
|
scenario
|
||||||
.args(&["--header=new file", test_file_path])
|
.args(&["--header=new file", test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
|
expected_test_file_path,
|
||||||
|
vec![
|
||||||
(&"{last_modified_time}".to_string(), &value),
|
(&"{last_modified_time}".to_string(), &value),
|
||||||
(&"{header}".to_string(), &header.to_string())
|
(&"{header}".to_string(), &header.to_string()),
|
||||||
]);
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -110,9 +132,10 @@ fn test_with_double_space_option() {
|
||||||
scenario
|
scenario
|
||||||
.args(&["-d", test_file_path])
|
.args(&["-d", test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &value),
|
expected_test_file_path,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -124,9 +147,10 @@ fn test_with_long_double_space_option() {
|
||||||
scenario
|
scenario
|
||||||
.args(&["--double-space", test_file_path])
|
.args(&["--double-space", test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &value),
|
expected_test_file_path,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -138,9 +162,10 @@ fn test_with_first_line_number_option() {
|
||||||
scenario
|
scenario
|
||||||
.args(&["-N", "5", "-n", test_file_path])
|
.args(&["-N", "5", "-n", test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &value),
|
expected_test_file_path,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -152,9 +177,10 @@ fn test_with_first_line_number_long_option() {
|
||||||
scenario
|
scenario
|
||||||
.args(&["--first-line-number=5", "-n", test_file_path])
|
.args(&["--first-line-number=5", "-n", test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &value),
|
expected_test_file_path,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -166,9 +192,10 @@ fn test_with_number_option_with_custom_separator_char() {
|
||||||
scenario
|
scenario
|
||||||
.args(&["-nc", test_file_path])
|
.args(&["-nc", test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &value),
|
expected_test_file_path,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -180,9 +207,10 @@ fn test_with_number_option_with_custom_separator_char_and_width() {
|
||||||
scenario
|
scenario
|
||||||
.args(&["-nc1", test_file_path])
|
.args(&["-nc1", test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &value),
|
expected_test_file_path,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -197,9 +225,7 @@ fn test_with_valid_page_ranges() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["--pages=1:5", test_file_path])
|
.args(&["--pages=1:5", test_file_path])
|
||||||
.succeeds();
|
.succeeds();
|
||||||
new_ucmd!()
|
new_ucmd!().args(&["--pages=1", test_file_path]).succeeds();
|
||||||
.args(&["--pages=1", test_file_path])
|
|
||||||
.succeeds();
|
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["--pages=-1:5", test_file_path])
|
.args(&["--pages=-1:5", test_file_path])
|
||||||
.fails()
|
.fails()
|
||||||
|
@ -227,15 +253,17 @@ fn test_with_page_range() {
|
||||||
scenario
|
scenario
|
||||||
.args(&["--pages=15", test_file_path])
|
.args(&["--pages=15", test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &value),
|
expected_test_file_path,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["--pages=15:17", test_file_path])
|
.args(&["--pages=15:17", test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path1, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &value),
|
expected_test_file_path1,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -259,9 +287,10 @@ fn test_with_page_length_option() {
|
||||||
scenario
|
scenario
|
||||||
.args(&["--pages=2:3", "-l", "100", "-n", test_file_path])
|
.args(&["--pages=2:3", "-l", "100", "-n", test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &value),
|
expected_test_file_path,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
|
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["--pages=2:3", "-l", "5", "-n", test_file_path])
|
.args(&["--pages=2:3", "-l", "5", "-n", test_file_path])
|
||||||
|
@ -288,9 +317,10 @@ fn test_with_stdin() {
|
||||||
.pipe_in_fixture("stdin.log")
|
.pipe_in_fixture("stdin.log")
|
||||||
.args(&["--pages=1:2", "-n", "-"])
|
.args(&["--pages=1:2", "-n", "-"])
|
||||||
.run()
|
.run()
|
||||||
.stdout_is_templated_fixture(expected_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &now_time()),
|
expected_file_path,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &now_time())],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -302,9 +332,10 @@ fn test_with_column() {
|
||||||
scenario
|
scenario
|
||||||
.args(&["--pages=3:5", "--column=3", "-n", test_file_path])
|
.args(&["--pages=3:5", "--column=3", "-n", test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &value),
|
expected_test_file_path,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -316,9 +347,10 @@ fn test_with_column_across_option() {
|
||||||
scenario
|
scenario
|
||||||
.args(&["--pages=3:5", "--column=3", "-a", "-n", test_file_path])
|
.args(&["--pages=3:5", "--column=3", "-a", "-n", test_file_path])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &value),
|
expected_test_file_path,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -328,11 +360,19 @@ fn test_with_column_across_option_and_column_separator() {
|
||||||
let mut scenario = new_ucmd!();
|
let mut scenario = new_ucmd!();
|
||||||
let value = file_last_modified_time(&scenario, test_file_path);
|
let value = file_last_modified_time(&scenario, test_file_path);
|
||||||
scenario
|
scenario
|
||||||
.args(&["--pages=3:5", "--column=3", "-s|", "-a", "-n", test_file_path])
|
.args(&[
|
||||||
|
"--pages=3:5",
|
||||||
|
"--column=3",
|
||||||
|
"-s|",
|
||||||
|
"-a",
|
||||||
|
"-n",
|
||||||
|
test_file_path,
|
||||||
|
])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &value),
|
expected_test_file_path,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -345,23 +385,35 @@ fn test_with_mpr() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["--pages=1:2", "-m", "-n", test_file_path, test_file_path1])
|
.args(&["--pages=1:2", "-m", "-n", test_file_path, test_file_path1])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &now_time()),
|
expected_test_file_path,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &now_time())],
|
||||||
|
);
|
||||||
|
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["--pages=2:4", "-m", "-n", test_file_path, test_file_path1])
|
.args(&["--pages=2:4", "-m", "-n", test_file_path, test_file_path1])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path1, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &now_time()),
|
expected_test_file_path1,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &now_time())],
|
||||||
|
);
|
||||||
|
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["--pages=1:2", "-l", "100", "-n", "-m", test_file_path, test_file_path1, test_file_path])
|
.args(&[
|
||||||
|
"--pages=1:2",
|
||||||
|
"-l",
|
||||||
|
"100",
|
||||||
|
"-n",
|
||||||
|
"-m",
|
||||||
|
test_file_path,
|
||||||
|
test_file_path1,
|
||||||
|
test_file_path,
|
||||||
|
])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path2, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &now_time()),
|
expected_test_file_path2,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &now_time())],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -380,7 +432,6 @@ fn test_with_mpr_and_column_options() {
|
||||||
.stdout_is("");
|
.stdout_is("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_with_offset_space_option() {
|
fn test_with_offset_space_option() {
|
||||||
let test_file_path = "column.log";
|
let test_file_path = "column.log";
|
||||||
|
@ -388,9 +439,18 @@ fn test_with_offset_space_option() {
|
||||||
let mut scenario = new_ucmd!();
|
let mut scenario = new_ucmd!();
|
||||||
let value = file_last_modified_time(&scenario, test_file_path);
|
let value = file_last_modified_time(&scenario, test_file_path);
|
||||||
scenario
|
scenario
|
||||||
.args(&["-o", "5", "--pages=3:5", "--column=3", "-a", "-n", test_file_path])
|
.args(&[
|
||||||
|
"-o",
|
||||||
|
"5",
|
||||||
|
"--pages=3:5",
|
||||||
|
"--column=3",
|
||||||
|
"-a",
|
||||||
|
"-n",
|
||||||
|
test_file_path,
|
||||||
|
])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_templated_fixture(expected_test_file_path, vec![
|
.stdout_is_templated_fixture(
|
||||||
(&"{last_modified_time}".to_string(), &value),
|
expected_test_file_path,
|
||||||
]);
|
vec![(&"{last_modified_time}".to_string(), &value)],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue