From 6751d2c70865c3881a5cddf97a0053f754245ded Mon Sep 17 00:00:00 2001 From: David Laban Date: Tue, 2 Aug 2016 10:29:24 +0100 Subject: [PATCH] implement sort --stable Made a new function sort_by(lines, compare_fns), which accepts a list of compare_fns and calls lines.sort_by() with a closure that calls each compare_fn in turn until one returns something other than equal. Default behavior ensures that String::cmp is the last element in the compare_fns list (referred to as 'last resort' sorting by man sort). Passing --stable (-s) turns this behaviour off. Test cases provided for `sort --month` and `sort --month --stable`. --- src/sort/sort.rs | 41 ++++++++++++++++++---- tests/fixtures/sort/month_default.expected | 3 ++ tests/fixtures/sort/month_default.txt | 3 ++ tests/fixtures/sort/month_stable.expected | 10 ++++++ tests/fixtures/sort/month_stable.txt | 10 ++++++ tests/test_sort.rs | 5 +++ 6 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 tests/fixtures/sort/month_stable.expected create mode 100644 tests/fixtures/sort/month_stable.txt diff --git a/src/sort/sort.rs b/src/sort/sort.rs index 28e2251cf..39c3bff9b 100644 --- a/src/sort/sort.rs +++ b/src/sort/sort.rs @@ -43,6 +43,7 @@ struct Settings { mode: SortMode, reverse: bool, outfile: Option, + stable: bool, unique: bool, check: bool, } @@ -53,6 +54,7 @@ impl Default for Settings { mode: SortMode::Default, reverse: false, outfile: None, + stable: false, unique: false, check: false, } @@ -70,6 +72,7 @@ pub fn uumain(args: Vec) -> i32 { opts.optflag("h", "help", "display this help and exit"); opts.optflag("", "version", "output version information and exit"); opts.optopt("o", "output", "write output to FILENAME instead of stdout", "FILENAME"); + opts.optflag("s", "stable", "stabilize sort by disabling last-resort comparison"); opts.optflag("u", "unique", "output only the first of an equal run"); opts.optflag("V", "version-sort", "Sort by SemVer version number, eg 1.12.2 > 1.1.2"); opts.optflag("c", "check", "check for sorted input; do not sort"); @@ -112,6 +115,7 @@ With no FILE, or when FILE is -, read standard input.", NAME, VERSION); settings.reverse = matches.opt_present("reverse"); settings.outfile = matches.opt_str("output"); + settings.stable = matches.opt_present("stable"); settings.unique = matches.opt_present("unique"); settings.check = matches.opt_present("check"); @@ -146,14 +150,25 @@ fn exec(files: Vec, settings: &Settings) -> i32 { let original_lines = lines.to_vec(); - match settings.mode { - SortMode::Numeric => lines.sort_by(numeric_compare), - SortMode::HumanNumeric => lines.sort_by(human_numeric_size_compare), - SortMode::Month => lines.sort_by(month_compare), - SortMode::Version => lines.sort_by(version_compare), - SortMode::Default => lines.sort() + let mut compare_fns = Vec::new(); + + compare_fns.push(match settings.mode { + SortMode::Numeric => numeric_compare, + SortMode::HumanNumeric => human_numeric_size_compare, + SortMode::Month => month_compare, + SortMode::Version => version_compare, + SortMode::Default => String::cmp + }); + + if !settings.stable { + match settings.mode { + SortMode::Default => {} + _ => compare_fns.push(String::cmp) + } } + sort_by(&mut lines, compare_fns); + if settings.unique { lines.dedup() } @@ -178,6 +193,20 @@ fn exec(files: Vec, settings: &Settings) -> i32 { } +fn sort_by(lines: &mut Vec, compare_fns: Vec) + where F: Fn( &String, &String ) -> Ordering +{ + lines.sort_by(|a, b| { + for compare_fn in &compare_fns { + let cmp = compare_fn(a, b); + if cmp != Ordering::Equal { + return cmp; + } + } + return Ordering::Equal; + }) +} + /// Parse the beginning string into an f64, returning -inf instead of NaN on errors. fn permissive_f64_parse(a: &str) -> f64 { // Maybe should be split on non-digit, but then 10e100 won't parse properly. diff --git a/tests/fixtures/sort/month_default.expected b/tests/fixtures/sort/month_default.expected index f3c462bc3..dc51f5d5e 100644 --- a/tests/fixtures/sort/month_default.expected +++ b/tests/fixtures/sort/month_default.expected @@ -3,5 +3,8 @@ Jan Lorem ipsum dolor sit amet mar laboris nisi ut aliquip ex ea May sed do eiusmod tempor incididunt JUN nostrud exercitation ullamco +Jul 1 should remain 2,1,3 +Jul 2 these three lines +Jul 3 if --stable is provided Oct ut labore et dolore magna aliqua Dec consectetur adipiscing elit diff --git a/tests/fixtures/sort/month_default.txt b/tests/fixtures/sort/month_default.txt index da4a02a71..6d64bf4f8 100644 --- a/tests/fixtures/sort/month_default.txt +++ b/tests/fixtures/sort/month_default.txt @@ -5,3 +5,6 @@ Oct ut labore et dolore magna aliqua N/A Ut enim ad minim veniam, quis JUN nostrud exercitation ullamco mar laboris nisi ut aliquip ex ea +Jul 2 these three lines +Jul 1 should remain 2,1,3 +Jul 3 if --stable is provided diff --git a/tests/fixtures/sort/month_stable.expected b/tests/fixtures/sort/month_stable.expected new file mode 100644 index 000000000..868728a18 --- /dev/null +++ b/tests/fixtures/sort/month_stable.expected @@ -0,0 +1,10 @@ +N/A Ut enim ad minim veniam, quis +Jan Lorem ipsum dolor sit amet +mar laboris nisi ut aliquip ex ea +May sed do eiusmod tempor incididunt +JUN nostrud exercitation ullamco +Jul 2 these three lines +Jul 1 should remain 2,1,3 +Jul 3 if --stable is provided +Oct ut labore et dolore magna aliqua +Dec consectetur adipiscing elit diff --git a/tests/fixtures/sort/month_stable.txt b/tests/fixtures/sort/month_stable.txt new file mode 100644 index 000000000..6d64bf4f8 --- /dev/null +++ b/tests/fixtures/sort/month_stable.txt @@ -0,0 +1,10 @@ +Jan Lorem ipsum dolor sit amet +Dec consectetur adipiscing elit +May sed do eiusmod tempor incididunt +Oct ut labore et dolore magna aliqua +N/A Ut enim ad minim veniam, quis +JUN nostrud exercitation ullamco +mar laboris nisi ut aliquip ex ea +Jul 2 these three lines +Jul 1 should remain 2,1,3 +Jul 3 if --stable is provided diff --git a/tests/test_sort.rs b/tests/test_sort.rs index 321af096b..2ec1904fa 100644 --- a/tests/test_sort.rs +++ b/tests/test_sort.rs @@ -37,6 +37,11 @@ fn test_month_default() { test_helper("month_default", "-M"); } +#[test] +fn test_month_stable() { + test_helper("month_stable", "-Ms"); +} + #[test] fn test_default_unsorted_ints() { test_helper("default_unsorted_ints", "");